1) Definición

La primera actividad de cualquier análisis y pronostico es definir el problema

En el libro Forecasting: Principles and Practice, capítulo primero, describe que es un pronóstico, que se puede pronosticar y cómo definir el problema, y algunos casos de estudios.

Lee más en el capítulo https://otexts.com/fpp3/determining-what-to-forecast.html

Descripción del las remesas

Es el envío de dinero de aquellas personas que radican en otra nación a su país de origen. Por ejemplo, los envíos de dinero que realizan los mexicanos que radican en Estados Unidos y Canadá a sus familias que viven en México.

Con el surgimiento de nuevas herramientas tecnológicas es más sencillo realizar esta transacción y el número de usuarios que la hacen se ha incrementado en los últimos años.

También existen las remesas enviadas por personas que se dedican a los negocios y tienen que pagar sueldos a los empleados contratados en el exterior, por ejemplo. Otra transferencia muy común es para estudiantes que no pueden trabajar y necesitan mantenerse en otro país mientras estudian.

Las remesas son la segunda fuente de divisas más importante de México después de los ingresos por petróleo. Representan cerca de 3% del PIB, 50% de las exportaciones petroleras, 135% de la inversión extranjera directa y 189% de los ingresos de viajeros internacionales.

Los ingresos por remesas provenientes del exterior ascendieron a 4,760 millones de dólares en diciembre de 2021, lo que implicó un aumento anual de 30.4%.

Por su parte, las remesas enviadas por residentes en México al exterior mostraron un crecimiento anual de 1.8%, al alcanzar un nivel de 114 millones de dólares.

Con estos resultados, el superávit de la cuenta de remesas de México con el resto del mundo fue de 4,646 millones de dólares, mayor al de 3,538 millones de dólares que se presentó en diciembre de 2020.

Con series desestacionalizadas, en el último mes de 2021 los ingresos y egresos por remesas exhibieron retrocesos mensuales de 1.9 y 2.3%, en igual orden. Así, en diciembre de 2021 el superávit de la cuenta de remesas se situó en 4,600 millones de dólares, que se compara con el de 4,687 millones de dólares que se observó en noviembre de 2021.

Para la totalidad de 2021, el valor de los ingresos por remesas fue de 51,594 millones de dólares, monto superior al de 40,605 millones de dólares reportado en 2020 y que significó una expansión anual de 27.1%.

Durante 2021, el 98.9% del total de los ingresos por remesas se realizó a través de transferencias electrónicas, al ubicarse en 51,045 millones de dólares. Por su parte, las remesas efectuadas en efectivo y especie2 y las money orders representaron el 0.7 y 0.4% del monto total, respectivamente, al registrar niveles de 333 y 216 millones de dólares, en el mismo orden.

Los egresos por remesas sumaron 1,057 millones de dólares en 2021, cifra mayor a la de 899 millones de dólares observada en 2020 y que implicó un incremento anual de 17.6%.

Con estos resultados, el saldo superavitario de la cuenta de remesas en 2021 fue de 50,537 millones de dólares, cifra superior a la de 39,706 millones de dólares reportada en 2020 y que representó un avance anual de 27.3%.

Datos

Banco de México registra las remesas como parte de la balanza de pagos y tiene registro mensual desde 1995.

Los datos de Banxico pueden ser descargados en el siguiente enlace

Determinar que pronosticar

Se analizara el ingreso total de remesas, como se muestra en la captura siguiente.

Por que es importante analizar y pronosticar en el contexto social

El flujo de remesas internacionales tiene un efecto importante sobre la actividad económica en algunas entidades que se localizan en las regiones centrales y el sur del país. Esto condujo a que el incremento observado en las remesas haya inducido en 2016 un mayor crecimiento económico en estas regiones.

En efecto, como resultado de los patrones de gasto de los hogares, dichas entidades tienden a ser más propensas a concentrar el impacto de las remesas en las actividades comerciales y los servicios. Adicionalmente, los flujos de remesas contribuyen a suavizar el ingreso de los hogares ante choques sobre la actividad económica, en particular en el sector agropecuario.

Las remesas son la segunda fuente de divisas más importante de México. En los últimos años las remesas han alcanzado máximos históricos, además, han obtenido mayor atención por las recientes investigaciones que señalan cómo cárteles del narco lavan dinero vía remesas.

2) Datos

Importar

En las siguientes celdas se muestan los pasos para importar los datos desde mi espacio de trabajo. Por que es un archivo en formato Excel, la librería usada es readxl.

Lee más en el notebook introductorio visto en clase: tidyverse, sección Data science workflow, paso 1 Import Data.

También en el libro R for Data Science capítulo 20.

library(readxl)

#raw_data = read_xlsx(path = "/Users/danielnuno/GitHub/time_series_s2024/Analisis/remesas banxico.xlsx", sheet = "Hoja1", col_names = FALSE)
raw_data = read_xlsx(path = "~/GitHub/time_series_s2024/Analisis/remesas/remesas banxico.xlsx", sheet = "Hoja1", col_names = FALSE)
New names:
head(raw_data, 20)

Eliminar filas

Los datos no inician hasta la fila 18. Usando dplyr::slice se pueden eliminan las primeras 18 filas.

Lee más en el notebook introductorio visto en clase: tidyverse, sección Data science workflow, paso 3 understand your data.

También en el libro R for Data Science capítulo 27.

#raw_data = raw_data[-(1:18), ]
data = dplyr::slice(raw_data, -(1:18))
head(data, 2)

Renombra las columnas

Las dimensiones están nombradas como …1 y …2, por lo tanto es conveniente renombrarlas sin espacios ni caracteres especiales.

colnames(data) = c('date', 'value')
head(data, 2)

Cambia el tipo de dato de las columnas

Las columnas son chr pero date tiene que ser una fecha mensual y value son millones de dólares. Utiliza lubridate para hacer la transformación de la fecha o R básico.

Lee más en el notebook introductorio visto en clase: tidyverse, sección Data science workflow, paso 3 understand your data: data transformation.

También en el libro R for Data Science capítulo 17.

Transforma el tipo texto cómo numérico

data$date = as.numeric(data$date)

Transforma el número a fecha utilizando el argumento origin

El número representa un día en la serie que inicia el primero de enero de 1990. Por lo tanto utilizamos el argumento origin que indica el inicio de la serie.

data$date = as.Date(data$date, origin = "1899-12-30")

Crea una secuencía explicita la periodicidad mensual

data$date = seq(from = min(data$date), to = max(data$date), by = "1 month")
library(lubridate)
library(tsibble)
data$date = yearmonth(data$date)
head(data, 2)

Cambia el tipo de dato de la columna value

data$value = as.numeric(data$value)
head(data, 2)

Crea un tsibble

Usando tsibble::as_tsibble() asigna la fecha como index. En este caso key no es necesario pero decide si es importante para tu serie.

Lee más en el notebook introductorio visto en clase: tidyverse, sección Data science workflow, paso 2 Tidy Data. Y el ejemplo 1.

También la documentación tsibble y el libro Forecast: Principles and Practice capítulo 2.1

data = as_tsibble(data, index=date, regular=TRUE)
head(data, 2)
interval(data)
<interval[1]>
[1] 1M

Valores faltantes y factores

Identifica si tu serie de tiempo tiene valores faltantes o factores y has las transformaciones correspondientes con ayuda del libro R for data science - Transform.

3) Análisis

Visualización de la serie

Utiliza ggplot2 para gráficar la serie

Lee más en el notebook introductorio visto en clase: tidyverse, sección Data science workflow, paso 3 Understand your data - visualization.

Ve los ejemplos y explicaciones vistas en clase: ggplot2, sección Gráficas con ggplot2.

También el libro Forecast: Principles and Practice capítulo 2.2

Time plot

library(ggplot2)
library(feasts)
feasts::autoplot(data) + ggtitle('Remesas por mes') + ylab('Millones de dólares') + xlab('Fecha')
Plot variable not specified, automatically selected `.vars = value`

Patrones

Gráficamente presenta tendencia, estacionalidad y heteroscedasticidad. Parece presentar ciclos relacionados a la actividad económica.

Consulta el libro Forecast: Principles and Practice capítulo 2.3

Gráfica estacionales

Una gráfica estacional es lo mismo que un time plot pero graficada usando los periodos estacionales. En este caso la estacionalidad es mensual.

Consulta el libro Forecast: Principles and Practice capítulo 2.4

data %>% gg_season(value, labels = "both") +
    ggtitle('Remesas por año') + ylab('Millones de dólares') + xlab('Mes')

Utiliza plotly::ggplotly() hacer gráficos interactivos

suppressWarnings(library(plotly))

yearly_data_plot = data %>% gg_season(value, labels = "both") +
    ggtitle('Remesas por año') + ylab('Millones de dólares') + xlab('Mes')

ggplotly(yearly_data_plot)

Sub gráficas estacionales

Un gráfico alternativo que enfatiza los patrones estacionales es aquel en el que los datos de cada temporada se recopilan en minigráficos de tiempo separados.

Consulta el libro Forecast: Principles and Practice capítulo 2.5

subseries_plot = data %>% gg_subseries(value)
ggplotly(subseries_plot)

Gráfico de rezagos

Estos gráficos son gráficos de dispersión evaluados en los rezagos de la misma serie que miden la correlación entre periodos. En otras palabras, para esta serie de tiempo mensual, mide la relación de un mes en el tiempo contra un mes (n lags) del pasado.

Consulta el libro Forecast: Principles and Practice capítulo 2.7

lags_plots = data %>% filter(year(date) > 2018) %>% gg_lag(value, geom = "point", lags = 1:12) + labs(x = "lag(Remesa, k)")

suppressWarnings(ggplotly(lags_plots))

Autocorrelación

Así como la correlación mide el alcance de una relación lineal entre dos variables, la autocorrelación mide la relación lineal entre valores rezagados de una serie temporal.

Consulta el libro Forecast: Principles and Practice capítulo 2.8

data %>% ACF(value, lag_max = 12)
data %>% ACF(value, lag_max = 24) %>% autoplot() + labs(title='Remesas por año')

Cuando los datos tienen tendencia, las autocorrelaciones para desfases pequeños tienden a ser grandes y positivas porque las observaciones cercanas en el tiempo también lo son en valor. Por lo tanto, el ACF de una serie temporal con tendencia tiende a tener valores positivos que disminuyen lentamente a medida que aumentan los rezagos.

Cuando los datos son estacionales, las autocorrelaciones serán mayores para los rezagos estacionales (en múltiplos del período estacional) que para otros rezagos.

Sera importante realizar las mismas gráficas una vez que la serie sea estacional.

Además de lo descrito en la primera parte del documento, con las gráficas podemos observar lo siguiente:

Gráficamente presenta tendencia, estacionalidad. Parece presentar ciclos relacionados a la actividad económica.

es interesante observar con este gráfico que a partir del año 2004 los datos se ven separados, es decir, la amplitud de los datos se vuelve más grande en cada año después que pasa. Podemos concluir tres cosas con esto:

  • Los primeros siete u ocho años de la serie hay crecimiento, pero a partir del 2003 el crecimiento es exponencial de cómo era antes.
  • En esas fechas se volvió más fácil hacer transferencias electrónicas y por lo tanto contabilizarlo en la balanza de pagos.
  • El crecimiento puede ser también por la solidaridad de los migrantes.

En el 2001, en respuesta a la “Sociedad para la prosperidad” entre México y Estados Unidos, el Banco de México y los Bancos de la Reserva Federal de los Estados Unidos acordaron estudiar la posibilidad de interconectar sus sistemas de pagos. La interconexión ha provisto a nuestras economías de un mecanismo eficiente para intercambiar pagos entre cuentas bancarias de ambos países.

En octubre de 2003, los Bancos de la Reserva Federal de los Estados Unidos y el Banco de México conectaron sus sistemas de pago para el envío de los pagos de los pensionados del Gobierno de los Estados Unidos que radican en México.

Posteriormente, desde el 2 de febrero de 2004, los usuarios de las instituciones financieras de los Estados Unidos suscritas a Directo a México pueden enviar pagos a cualquier cuenta bancaria en México.

Desde julio de 2005, este servicio de pagos se registró con el nombre Directo a México

Entonces para considerar como periodos que han afectado la serie de tiempo tenemos:

  • 2003-10 facilidad de transferencias electrónicas
  • 2007-12, 2009-06 recesión global por crisis hipotecaria
  • 2020-02, 2020-04 recesión global por COVID-19
  • Apartir del 2018 hay un cambio de tendencia y el crecimieto es acelerado.

Con los gráficos de rezagos podemos apreciar que muestra mayor correlación con el periodo inmediato anterior (1) y el mismo periodo del año pasado (12). Es decir, se espera que se envié una similar cantidad de dinero comparada a estos dos periodos del pasado (1 y 12), pero para el periodo 12 se espera que se envié más dinero.

Estadística descriptiva

Medidas de tendencia central

Calcula la media, moda y mediana. Estos estadisticos pueden ser de la totalidad de la serie o por año o por periodo estacional.

print(paste('fecha inicial', min(data$date)))
[1] "fecha inicial 1995 Jan"
print(paste('fecha final', max(data$date)))
[1] "fecha final 2023 Dec"
print(paste('observaciones', nrow(data)))
[1] "observaciones 348"
print(paste('existen', sum(is.na(data)), 'datos faltantes'))
[1] "existen 0 datos faltantes"
summary(data[, 'value'])
     value       
 Min.   : 248.1  
 1st Qu.: 797.8  
 Median :1933.9  
 Mean   :1993.3  
 3rd Qu.:2453.4  
 Max.   :5817.8  

Observamos que, por mes, el promedio de millones de dólares es de $1,993.2 millones de dólares, el mínimo fue de $248.1 millones de dólares, mientras que el máximo fue de $5,817.8 millones.

boxplot = data %>% 
            mutate(year = year(date)) %>% 
            ggplot(aes(x = as.factor(year), y = value)) + 
            geom_boxplot() + 
            xlab('Año') + 
            ylab('Remesas')

ggplotly(boxplot)

Medidas de dispersión

Estos estadísticos que nos dan información al respecto de la variabilidad o separación de los datos generalmente respecto a la media. Nos ayudan a comprender la distribución de los datos y evitan tomar conclusiones erróneas al comparar distintos grupos.

sd(data$value)
[1] 1313.988
var(data$value)
[1] 1726565
library(EnvStats)
kurtosis(data$value)
[1] 0.5389753
skewness(data$value)
[1] 0.8972091
shapiro.test(data$value)

    Shapiro-Wilk normality test

data:  data$value
W = 0.91084, p-value = 1.773e-13
library(ggExtra)
p <- ggplot(data, aes(x=date, y=value)) + 
        geom_hline(yintercept =1000) + 
        geom_hline(yintercept =3000) +
        geom_point() + 
        ggtitle('Remesas por mes') + ylab('Millones de dólares') + xlab('Fecha')

ggMarginal(p, type='histogram', margins = 'y')

histogram = ggplot(data, aes(x = value)) +
  geom_histogram( bins = 20, fill = "black", color = "black", alpha = 0.5) +
  labs(title = "Histograma",
       x = "Value",
       y = "Densidad")

ggplotly(histogram)

La distribución presenta un sesgo de simetría negativo a la izquierda y aunque los valores no son tan grandes, alguna transformación sería útil antes de hacer la descomposición de la serie y los pronósticos.

Valores atípicos

La detección de outliers es importante para afinar nuestro pronóstico y eliminar las observaciones atípicas. Existen diferentes formas de definir valores atípicos. En este caso, si los outliers son 1.5 veces el rango intercuartílico por encima y debajo del 1 y 3 cuartil, sí hay valores atípicos.

ttl_m_dlrs <- data %>% select('value')
ttl_m_dlrs <- as.numeric(unlist(ttl_m_dlrs[,1]))
summary(ttl_m_dlrs)[2] - 1.5*IQR(ttl_m_dlrs) >= summary(ttl_m_dlrs)[1]
1st Qu. 
  FALSE 
summary(ttl_m_dlrs)[5] + 1.5*IQR(ttl_m_dlrs) <= summary(ttl_m_dlrs)[6]
3rd Qu. 
   TRUE 
summary(ttl_m_dlrs)[2] - 3*IQR(ttl_m_dlrs) >= summary(ttl_m_dlrs)[1]
1st Qu. 
  FALSE 
summary(ttl_m_dlrs)[5] + 3*IQR(ttl_m_dlrs) <= summary(ttl_m_dlrs)[6]
3rd Qu. 
  FALSE 
library(tidyverse)

p <- data %>% as_tibble %>% group_by(years=year(date)) %>%
    summarise(remesas=sum(value)) %>%
    arrange(desc(years))%>%
    mutate(change = (remesas/lead(remesas) - 1) * 100) %>% 
    filter(years > 1995) %>% 
    filter(years < 2023)

mean_growth <- data %>% as_tibble %>% group_by(years=year(date)) %>%
                    summarise(remesas=sum(value)) %>%
                    arrange(desc(years))%>%
                    mutate(change = (remesas/lead(remesas) - 1) * 100) %>% 
                    filter(years > 1995) %>% 
                    filter(years < 2022) %>%
                    summarise(mean(change))

mean_growth <- mean_growth$`mean(change)`

ggplot(p, aes(x=years, y=change)) +
    geom_line() +
    geom_hline(yintercept=mean_growth) +
    geom_hline(yintercept=0) +
    ggtitle('Cambio porcentual por año') + ylab('%') + xlab('Mes')

El crecimiento promedio es de 11.48%.

4) Pronósticos base

Define los periodos de prueba y entrenamiento

Dependiendo de la cantidad de datos y la periodicidad podrías definir diferente cantidad de observaciones. La regla de pulgar es usar 80% de los datos para entrenar y el restante 20% para validar los resultados.

Para esta serie de tiempo 6 meses de validación son suficientes.

También en el libro Forecast: Principles and Practice capítulo 5

train <- data %>% select(value) %>% filter_index("1995 Jan" ~ "2023 Jun")
test <- data %>% select(value) %>% filter_index("2023 Jul" ~ "2023 Dec")
tstng_prds <- 6
frcst_prds <- 6

Seasonal Naive

En el libro Forecast: Principles and Practice capítulo 5, y notebook de clase pronósticos base se muestran diferentes métodos iniciales para hacer estimaciones. Estos métodos iniciales son ligados a la experiencia en la serie y el análisis exploratorio realizado con anterioridad.

Utilizando la funciónfable::SNAIVE para el primer pronóstico base debido a que hay una clara estacionalidad mensual.

library(fable)
models_fit <- train %>% 
    model(`Seasonal naive` = SNAIVE(value))
models_tst <- models_fit %>% forecast(h = tstng_prds)
snaive_plot <- models_tst %>% autoplot(filter_index(data, "2018 Jan" ~ .)) +
    ggtitle('Seasonal Naive') + ylab('Remesas') + xlab('Mes')

snaive_plot

Intervalos de predicción

models_tst
models_tst %>% hilo(level = c(80, 95))

Errores de pronóstico

accuracy(models_fit)
(models_fit %>% forecast(h = tstng_prds) %>% accuracy(test))

Diagnostico de resiuales

aug = augment(models_fit)
aug
aug %>% pull(.resid) %>% mean(na.rm = TRUE)
[1] 175.1076
aug %>% autoplot(.resid) + xlab("Mes") + ylab("") +
  ggtitle("Residuales del método seasonal naïve")

aug %>%
  ggplot(aes(x = .resid)) +
  geom_histogram() +
  ggtitle("Histograma de los residuales")

aug %>% ACF(.resid)
aug %>% ACF(.resid) %>% autoplot() + ggtitle("ACF of residuals")

train %>% 
  model(SNAIVE(value)) %>% 
  gg_tsresiduals()

Observamos que estas gráficas tienen un comportamiento muy distinto al Naïve:

  • En la gráfica de los residuales vemos que se distingue claramente un patrón. De hecho, es el mismo patrón exactamente que siguen los datos originales, restándoles su media.

  • La función de autocorrelación tiene un comportamiento típico de una caminata aleatoria. Por lo tanto, las autocorrelaciones son significativas.

  • El histograma de los residuos muestra claramente que no se distribuyen de manera normal.

Test de Ljung-Box

Un test relacionado y que, generalmente, es más preciso es el test de Ljung-Box.

En este caso es igual: valores grandes de la prueba son indicios de que las autocorrelaciones no provienen de ruido blanco.

Entonces, la hipótesis nula de estas pruebas es que la serie en cuestión no está autocorrelacionada. En otras palabras, la H0 dice que la serie es ruido blanco. Si α es el nivel de significancia (el nivel máximo de error que estamos dispuestos a aceptar) y si el ¨p-value <α, entonces rechazamos H0, de lo contrario, no rechazamos la H0.

aug %>% features(.resid, ljung_box, lag=12, dof=0)

5) Descomposición

Componentes y descomposición STL

Consulta el libro Forecast: Principles and Practice capítulo 3.6

Para estudiar las características de tendencia y estacionalidad se descompone y graficar usando el método STL. La función per de periodicity es la más adecuada al parámetro de suavización estacional s.windows. Es el componente estacional es el más pequeño, siendo la tendencia más significativa.

Podemos observar en el componente estacional captura, que el inicio de año es el más flojito y que, a mediados del año es cuando se envían más remesas.

El competente de tendencia y remanentes observamos y confirmamos lo mencionado anteriormente. A partir del 2004 el envío de remesas, o el registro, aumenta considerablemente y el en 2008 y 2020 el remanente es muy significativo. También, a partir del 2020 el componente remanente se acentúa.

Cleveland, R. B., Cleveland, W. S., McRae, J. E., & Terpenning, I. J. (1990). STL: A seasonal-trend decomposition procedure based on loess. Journal of Official Statistics, 6(1), 3–33. http://bit.ly/stl1990

stl_model = data %>% dplyr::select(value) %>% stl(s.window = 'per')
plot(stl_model,main = 'Descomposicón de la serie con STL')

Transformaciones y adjustes

Consulta el libro Forecast: Principles and Practice capítulo 3.1

Ajustar los datos históricos a menudo puede conducir a series temporales más simples. ejemplos de pueden ser ajustes de calendario, ajustes de población, ajustes de inflación y transformaciones matemáticas. El propósito de estos ajustes y transformaciones es simplificar los patrones en los datos históricos eliminando fuentes conocidas de variación o haciendo que el patrón sea más consistente en todo el conjunto de datos. Los patrones más simples suelen ser más fáciles de modelar y conducen a pronósticos más precisos.

Cómo ya se mencionó anteriormente con el histograma, y con observar las magnitudes de los valores al inicio y final de la serie es conveniente una transformación de potencia. El histograma y el sesgo también muestra sesgo negativo. Además, la prueba de normalidad SHAPIRO-WILK (alpha = 0.05)

qqnorm(data$value)
qqline(data$value)

Valor p es menor que Alpha entonces no tenemos una distribución normal. De hecho, estamos lejos de una distribución normal pero el sesgo no esta tan mal siendo 0.5. Queremos lo más acercado a 0 de sesgo.

Aunque se puede entender y usar la transformación box cox para transformar una distribución no normal a una normal, el principal objetivo es corregir sesgos y varianzas desiguales de la siguiente manera:

\[ y(\lambda) =\begin{cases} \frac{(y^\lambda - 1)}{\lambda}& \lambda \neq 0 ,\\ log(y)& \lambda = 0. \end{cases} \]

El lambda óptimo se encuentra cuando se encuentra la distribución con el máximo loglikelihood o con el coeficiente del gráfico de probabilidad de correlación o Shapiro-Wilk.

Bickel, P. J., & Doksum, K. A. (1981). An analysis of transformations revisited. Journal of the American Statistical Association, 76(374), 296–311. DOI

Box, G. E. P., & Cox, D. R. (1964). An analysis of transformations. Journal of the Royal Statistical Society. Series B, Statistical Methodology, 26(2), 211–252. DOI

bc <- EnvStats::boxcox(data$value, lambda=c(-2, 2), optimize=TRUE, objective.name='Log-Likelihood')
bc_data <- EnvStats::boxcoxTransform(data$value, bc$lambda)

skewness(bc_data)
[1] -0.07935633
shapiro.test(bc_data)

    Shapiro-Wilk normality test

data:  bc_data
W = 0.9521, p-value = 3.251e-09
qqnorm(bc_data)
qqline(bc_data)


data <- data %>% mutate('remesas_trn' = bc_data)

6) Pronósticos base con STL y tranformación matemática

STL Seasonal Naive

models_fit <- train %>% 
  model(stlf = decomposition_model(
    STL(value ~ trend(window = 12), robust = TRUE),
    NAIVE(season_adjust)
  ))
models_tst <- models_fit %>% forecast(h = tstng_prds)
mape_sn <- (models_fit %>% forecast(h = tstng_prds) %>% accuracy(test))$MAPE
snaive_plot <- models_tst %>% autoplot(filter_index(data, "2018 Jan" ~ .)) +
    ggtitle('STL') + ylab('Remesas') + xlab('Mes')

snaive_plot

?decomposition_model
models_fit <- train %>% 
  model(stlf = decomposition_model(
    STL(log(value) ~ trend(window = 12), robust = TRUE),
    NAIVE(season_adjust)
  ))
models_tst <- models_fit %>% forecast(h = tstng_prds)
mape_sn <- (models_fit %>% forecast(h = tstng_prds) %>% accuracy(test))$MAPE
snaive_plot <- models_tst %>% autoplot(filter_index(data, "2018 Jan" ~ .)) +
    ggtitle('Seasonal Naive') + ylab('Remesas') + xlab('Mes')

snaive_plot

models_fit <- train %>% 
  model(
    `Seasonal naive` = SNAIVE(value),
    stlf = decomposition_model(
    STL(value ~ trend(window = 12), robust = TRUE),
    NAIVE(season_adjust)),
    log_stlf = decomposition_model(
            STL(log(value) ~ trend(window = 12), robust = TRUE),
            NAIVE(season_adjust))
  )
models_tst <- models_fit %>% forecast(h = tstng_prds)
mape_sn <- (models_fit %>% forecast(h = tstng_prds) %>% accuracy(test))$MAPE
snaive_plot <- models_tst %>% autoplot(filter_index(data, "2018 Jan" ~ .), level = NULL) +
    ggtitle('Diferentes modelos') + ylab('Remesas') + xlab('Mes')

snaive_plot

models_tst
models_tst %>% hilo(level = c(80, 95))
accuracy(models_fit)
train %>% 
  model(decomposition_model(
            STL(log(value) ~ trend(window = 12), robust = TRUE),
            NAIVE(season_adjust))) %>% 
  gg_tsresiduals()

models_fit[3] %>% gg_tsresiduals()

7) Regresión Lineal

Datos

La hipótesis radica en la relación y crecimiento económico de Estados Unidos precede y esta correlacionado a el incremento de remesas. Por lo que el índice Brave-Butters-Kelley puede ser relevante para la estimación de las remesas.

The Brave-Butters-Kelley Indexes (BBKI) are a research project of the Federal Reserve Bank of Chicago. The BBK Coincident and Leading Indexes and Monthly GDP Growth for the U.S. are constructed from a collapsed dynamic factor analysis of a panel of 500 monthly measures of real economic activity and quarterly real GDP growth.

Los datos mensuales se pueden obtener de la FRED. https://fred.stlouisfed.org/series/BBKMGDP

Importar los datos usando tidyquant

library(tidyquant)
gdp_us = tq_get("BBKMGDP", get="economic.data", from = "1995-01-01")

Convertir la fecha y dataframe a tsibble

gdp_us$date = yearmonth(gdp_us$date)
gdp_us = gdp_us %>% select(date, price) %>% as_tsibble(index=date, regular = TRUE)
gdp_us

Convertir el cambio percentual a base 100 con la suma acumulada de los cambios porcentuales

gdp_us = gdp_us %>% mutate(gdp_base = cumsum(price))
gdp_us

Separa entre entrenamiento y prueba

train_gdp <- gdp_us %>% select(gdp_base) %>% filter_index("1995 Jan" ~ "2023 Jun")
test_gdp <- gdp_us %>% select(gdp_base) %>% filter_index("2023 Jul" ~ "2023 Dec")

Renombrar las columnas

train_gdp = add_column(train_gdp, train$value) #append remesas
colnames(train_gdp)[3] = "remesas" 
colnames(train_gdp)[1] = "gdp"
test_gdp = add_column(test_gdp, test$value)
colnames(test_gdp)[3] = "remesas"
colnames(test_gdp)[1] = "gdp"

Gráfica de ambas series a traves del tiempo

train_gdp |>
  pivot_longer(c(remesas, gdp), names_to="Series") |>
  autoplot(value) +
  labs(y = "value")

Gráfica de disperción (correlación) entre ambas series

train_gdp %>% ggplot(aes(x = gdp, y = log(remesas))) +
  labs(y = "Remesas",
       x = "GDP Base 1") +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

Ajuste de regresión y reporte del modelo

fit_lm <- train_gdp |>
  model(tslm = TSLM(log(remesas) ~ gdp))

report(fit_lm)
Series: remesas 
Model: TSLM 
Transformation: log(remesas) 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.47518 -0.16670 -0.03232  0.17573  0.58386 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 5.736e+00  2.848e-02  201.44   <2e-16 ***
gdp         3.479e-03  5.678e-05   61.26   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.227 on 340 degrees of freedom
Multiple R-squared: 0.9169, Adjusted R-squared: 0.9167
F-statistic:  3753 on 1 and 340 DF, p-value: < 2.22e-16

Tabla aumentada del ajuste del modelo

augment(fit_lm)

Valores calculados

plot_lm = augment(fit_lm) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = remesas, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Zoom a la gráfica despues de 2018

augment(fit_lm) |> filter(year(date) > 2018) %>%
  ggplot(aes(x = date)) +
  geom_line(aes(y = remesas, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))

Analisis de residuales

Primera gráfica: errores en el tiempo para evaluar media y varianza. Segunda gráfica: autocorrelación de errores: errores en el pasado afectan el valor actual. tercera gráfica: histograma de errores para verificar la normalidad, media sea 0 y sesgo sea 0.

fit_lm %>% gg_tsresiduals()

augment(fit_lm) %>% features(.innov, ljung_box, lag=12)

Los residuales estan autocorrelacionados, no son homocedasticos y la media de los errores no es cero.

Gráfica de residuales contra valores ajustados.

También sirve para identificar outliers

augment(fit_lm) %>% ggplot(aes(x=.fitted, y=.resid)) + geom_point() + labs(x="Fitted", y="Residuals")

Los datos de ambas variables no son estacionarias, es decir tienen tendencía. Y puede ser que ningua tenga relación con la otra ni sea causal de la otra.

Regresiones con relaciones espurias puede que funcionen en el corto plazo pero dejen de funcionar en el futuro.

Pronósticos

test_gdp[c("gdp", "date")]

Gráfica de prónosticos

fc_lm = forecast(fit_lm, new_data = test_gdp[c("gdp", "date")])
data %>% autoplot(value) + autolayer(fc_lm)

Table de prónosticos

fc_lm

Otras variables predictoras

Tendencia

\[ y_{t} = \beta_{0} + \beta_{1}t + \epsilon_{t} \] dónde $ t = {1,2,3, …, n} $

TSLM utiliza el argumento trend() para indicar la tendencia.

Estacionalidad

Para estacionalidad diaria, calcula 6 variables:

\[ y_{t} = \beta_{0} + \beta_{1}lunes + \beta_{2}martes + \beta_{3}miercoles + \beta_{4}jueves + \beta_{5}viernes + \beta_{6}sabado + \epsilon_{t} \]

Para estacionalidad mensual, calcula 11 variables:

\[ y_{t} = \beta_{0} + \beta_{1}enero + \beta_{2}febrero + \beta_{3}marzo + \beta_{4}abril + \beta_{5}mayo + \beta_{6}junio + \beta_{7}julio + \beta_{8}agosto + \beta_{9}septiembre + \beta_{10}octubre + \beta_{11}noviembre + \epsilon_{t} \]

Para estacionalidad trimestral calcula 3 variables. Para estacionalidad diaria calcula 23 variables. Para estacionalidad semanal calcula 51 variables.

TSLM utiliza el argumento season() para indicar la estacionalidad.

Atipicos/Spike/Intervention variable

Con lo observado en las gráfica y lo comentado anteriormente, las fechas de importancia para esta serie de tiempo son la crisis hipotecaria y la crisis covid. Definir una columna de 0s, exepto entre las fechas indicadas (diciembre 2007 - junio 2009). Definir una columna de 0s, exepto entre las fechas indicadas (febrero 2020 - mayo 2020).

Crea una variable dummy de valores atípicos para tu serie de tiempo

train_gdp = train_gdp %>%
    mutate(crisish = if_else(date >= as.Date("2007-12-01") & 
                               date <= as.Date("2009-06-01"), 1, 0),
           crisisc = if_else(date >= as.Date("2020-02-01") & 
                               date <= as.Date("2020-05-01"), 1, 0)
           )
Warning: There were 4 warnings in `mutate()`.
The first warning was:
ℹ In argument: `crisish = if_else(...)`.
Caused by warning in `is_logical()`:
! Incompatible methods (">=.vctrs_vctr", ">=.Date") for ">="
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 3 remaining warnings.
test_gdp = test_gdp %>%
    mutate(crisish = if_else(date >= as.Date("2007-12-01") & 
                               date <= as.Date("2009-06-01"), 1, 0),
           crisisc = if_else(date >= as.Date("2020-02-01") & 
                               date <= as.Date("2020-05-01"), 1, 0)
           )
Warning: There were 4 warnings in `mutate()`.
The first warning was:
ℹ In argument: `crisish = if_else(...)`.
Caused by warning in `is_logical()`:
! Incompatible methods (">=.vctrs_vctr", ">=.Date") for ">="
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 3 remaining warnings.

Rezagos

Usa los valores pasados cómo predictor de X. Hipotesis: Las remesas (o el cambio porcentual) enviadas en el pasado influyen en el futuro. Revisa la autocorrelación.

\(x_{1}\) = remesas enviadas en el mes previo.

\(x_{2}\) = remesas enviadas hace dos meses.

\(x_{m}\) = remesas enviadas hace m meses.

El siguiente código muestra como calcular una variable con rezagos = 1

train_gdp$lag1 = c(NA, train_gdp$remesas[1:length(train_gdp$remesas)-1])
train_gdp
test_gdp$lag1 = c(train_gdp$remesas[length(train_gdp$remesas)], test_gdp$remesas[1:length(test_gdp$remesas)-1])
test_gdp

fourier

fit_lm <- train_gdp |>
  model(tslm = TSLM(log(remesas) ~ fourier(K=2) ))

report(fit_lm)
Series: remesas 
Model: TSLM 
Transformation: log(remesas) 

Residuals:
    Min      1Q  Median      3Q     Max 
-1.7513 -0.6527  0.2549  0.4639  1.3582 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)          7.31039    0.04258 171.692   <2e-16 ***
fourier(K = 2)C1_12 -0.09054    0.06021  -1.504    0.134    
fourier(K = 2)S1_12  0.02135    0.06022   0.355    0.723    
fourier(K = 2)C2_12 -0.03742    0.06021  -0.621    0.535    
fourier(K = 2)S2_12 -0.03407    0.06021  -0.566    0.572    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.7873 on 337 degrees of freedom
Multiple R-squared: 0.009207,   Adjusted R-squared: -0.002553
F-statistic: 0.7829 on 4 and 337 DF, p-value: 0.53692
plot_lm = augment(fit_lm) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = remesas, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Fourier

fit_lm <- train_gdp |>
  model(tslm = TSLM(log(remesas) ~ trend() + fourier(K=6) ))

report(fit_lm)
Series: remesas 
Model: TSLM 
Transformation: log(remesas) 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.51478 -0.21600 -0.07182  0.11848  0.65234 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)          6.0477230  0.0313721 192.774  < 2e-16 ***
trend()              0.0073633  0.0001585  46.445  < 2e-16 ***
fourier(K = 6)C1_12 -0.0902607  0.0221349  -4.078 5.71e-05 ***
fourier(K = 6)S1_12  0.0212783  0.0221349   0.961   0.3371    
fourier(K = 6)C2_12 -0.0296631  0.0221354  -1.340   0.1811    
fourier(K = 6)S2_12 -0.0213924  0.0221366  -0.966   0.3346    
fourier(K = 6)C3_12 -0.0037863  0.0221349  -0.171   0.8643    
fourier(K = 6)S3_12 -0.0185609  0.0221349  -0.839   0.4023    
fourier(K = 6)C4_12 -0.0195143  0.0221354  -0.882   0.3786    
fourier(K = 6)S4_12 -0.0166023  0.0221351  -0.750   0.4538    
fourier(K = 6)C5_12 -0.0091343  0.0221349  -0.413   0.6801    
fourier(K = 6)S5_12 -0.0463210  0.0221349  -2.093   0.0371 *  
fourier(K = 6)C6_12 -0.0042256  0.0156519  -0.270   0.7873    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2894 on 329 degrees of freedom
Multiple R-squared: 0.8693, Adjusted R-squared: 0.8645
F-statistic: 182.4 on 12 and 329 DF, p-value: < 2.22e-16
plot_lm = augment(fit_lm) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = remesas, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Regresión lineal múltiple

fit_lm <- train_gdp |>
  model(tslm = TSLM(log(remesas) ~ trend() + season() + gdp + crisish + crisisc + fourier(K = 2)))

report(fit_lm)
Series: remesas 
Model: TSLM 
Transformation: log(remesas) 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.54428 -0.13482  0.01035  0.15911  0.37922 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)          5.4355637  0.0451541 120.378  < 2e-16 ***
trend()             -0.0042571  0.0006211  -6.854 3.61e-11 ***
season()year2        0.0170931  0.0496706   0.344 0.730971    
season()year3        0.1857560  0.0496679   3.740 0.000217 ***
season()year4        0.1730603  0.0496670   3.484 0.000561 ***
season()year5        0.2917365  0.0496664   5.874 1.05e-08 ***
season()year6        0.2336911  0.0495595   4.715 3.58e-06 ***
season()year7        0.2109345  0.0500205   4.217 3.21e-05 ***
season()year8        0.2199883  0.0500269   4.397 1.49e-05 ***
season()year9        0.1514828  0.0500320   3.028 0.002661 ** 
season()year10       0.1658213  0.0500363   3.314 0.001023 ** 
season()year11       0.0642423  0.0500380   1.284 0.200099    
season()year12       0.1289277  0.0500094   2.578 0.010374 *  
gdp                  0.0053772  0.0002823  19.046  < 2e-16 ***
crisish              0.2735281  0.0452035   6.051 3.95e-09 ***
crisisc              0.2360271  0.0993590   2.375 0.018103 *  
fourier(K = 2)C1_12         NA         NA      NA       NA    
fourier(K = 2)S1_12         NA         NA      NA       NA    
fourier(K = 2)C2_12         NA         NA      NA       NA    
fourier(K = 2)S2_12         NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1887 on 326 degrees of freedom
Multiple R-squared: 0.9449, Adjusted R-squared: 0.9424
F-statistic:   373 on 15 and 326 DF, p-value: < 2.22e-16

Selección de variables

glance(fit_lm) |>
  select(adj_r_squared, CV, AIC, AICc, BIC)
fit_lm <- train_gdp |>
  model(tslm = TSLM(log(remesas) ~ trend() + season() + gdp + crisish + crisisc + lag1))

report(fit_lm)
Series: remesas 
Model: TSLM 
Transformation: log(remesas) 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.48499 -0.11092 -0.01781  0.15005  0.36325 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     5.472e+00  4.303e-02 127.178  < 2e-16 ***
trend()        -5.233e-03  5.851e-04  -8.943  < 2e-16 ***
season()year2   6.024e-02  4.625e-02   1.303 0.193666    
season()year3   2.212e-01  4.615e-02   4.793 2.50e-06 ***
season()year4   1.550e-01  4.607e-02   3.364 0.000862 ***
season()year5   2.879e-01  4.599e-02   6.259 1.23e-09 ***
season()year6   1.916e-01  4.625e-02   4.142 4.40e-05 ***
season()year7   1.923e-01  4.639e-02   4.146 4.33e-05 ***
season()year8   2.078e-01  4.634e-02   4.484 1.02e-05 ***
season()year9   1.347e-01  4.637e-02   2.905 0.003921 ** 
season()year10  1.672e-01  4.630e-02   3.611 0.000353 ***
season()year11  5.491e-02  4.633e-02   1.185 0.236823    
season()year12  1.499e-01  4.632e-02   3.236 0.001338 ** 
gdp             5.007e-03  2.656e-04  18.853  < 2e-16 ***
crisish         2.375e-01  4.172e-02   5.692 2.81e-08 ***
crisisc         2.303e-01  9.117e-02   2.526 0.012006 *  
lag1            1.573e-04  1.984e-05   7.929 3.60e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1731 on 324 degrees of freedom
Multiple R-squared: 0.9532, Adjusted R-squared: 0.9509
F-statistic: 412.8 on 16 and 324 DF, p-value: < 2.22e-16
glance(fit_lm) |>
  select(adj_r_squared, CV, AIC, AICc, BIC)

Análisis de residuales

fit_lm %>% gg_tsresiduals()

8) Suavización exponencial

Simple

fit_es <- train |>
  model(ETS(value ~ error("A") + trend("N") + season("N")))

report(fit_es)
Series: value 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.5842231 

  Initial states:
     l[0]
 261.8688

  sigma^2:  50869.13

     AIC     AICc      BIC 
5705.757 5705.828 5717.262 

Alpha es de 0.58, lo qué indica que la última observación no es la más importante y las pasadas observaciones tienen un peso significativo

AICc es 5705

plot_lm = augment(fit_es) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = value, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Con tendencia

fit_es <- train |>
  model(AAN = ETS(value ~ error("A") + trend("A") + season("N")))

report(fit_es)
Series: value 
Model: ETS(A,A,N) 
  Smoothing parameters:
    alpha = 0.5587472 
    beta  = 0.0001000121 

  Initial states:
     l[0]     b[0]
 270.2258 15.16794

  sigma^2:  50455.56

     AIC     AICc      BIC 
5704.948 5705.126 5724.122 
plot_lm = augment(fit_es) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = value, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)
fit_es <- train |>
  model(Damped = ETS(value ~ error("A") + trend("Ad") + season("N")))

report(fit_es)
Series: value 
Model: ETS(A,Ad,N) 
  Smoothing parameters:
    alpha = 0.5691449 
    beta  = 0.004834991 
    phi   = 0.9799996 

  Initial states:
     l[0]     b[0]
 269.8862 9.707265

  sigma^2:  51170.91

     AIC     AICc      BIC 
5710.749 5711.000 5733.758 
plot_lm = augment(fit_es) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = value, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Con estacionalidad

fit_es <- train |>
  model(AAA = ETS(value ~ error("A") + trend("A") + season("A")))

report(fit_es)
Series: value 
Model: ETS(A,A,A) 
  Smoothing parameters:
    alpha = 0.2878418 
    beta  = 0.01257488 
    gamma = 0.3483385 

  Initial states:
     l[0]     b[0]      s[0]     s[-1]   s[-2]     s[-3]    s[-4]    s[-5]    s[-6]    s[-7]     s[-8]    s[-9]
 391.0324 2.686417 -5.107151 -123.8252 71.0204 -5.325298 107.7276 90.86807 148.2313 194.8573 -67.60189 59.09909
    s[-10]    s[-11]
 -244.5589 -225.3853

  sigma^2:  22715.69

     AIC     AICc      BIC 
5443.656 5445.545 5508.848 
plot_lm = augment(fit_es) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = value, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)
train
fit_es <- train |>
  model(AAA = ETS(value ~ error("M") + trend("A") + season("M")))

report(fit_es)
Series: value 
Model: ETS(M,A,M) 
  Smoothing parameters:
    alpha = 0.4929797 
    beta  = 0.01305041 
    gamma = 0.1295237 

  Initial states:
     l[0]     b[0]      s[0]     s[-1]     s[-2]    s[-3]    s[-4]    s[-5]   s[-6]    s[-7]    s[-8]     s[-9]
 291.2129 3.110346 0.9814982 0.8821721 0.9925036 1.001262 1.081113 1.054762 1.07494 1.147514 1.014973 0.9830665
    s[-10]    s[-11]
 0.8696745 0.9165203

  sigma^2:  0.0048

     AIC     AICc      BIC 
5186.587 5188.476 5251.779 
plot_lm = augment(fit_es) |>
  ggplot(aes(x = date)) +
  geom_line(aes(y = value, colour = "reales")) +
  geom_line(aes(y = .fitted, colour = "ajustados")) +
  labs(y = NULL,
    title = "remesas"
  ) +
  guides(colour = guide_legend(title = NULL))
ggplotly(plot_lm)

Selección del modelo

fit_es <- train |>
  model(ETS(value))

report(fit_es)
Series: value 
Model: ETS(M,A,M) 
  Smoothing parameters:
    alpha = 0.4929797 
    beta  = 0.01305041 
    gamma = 0.1295237 

  Initial states:
     l[0]     b[0]      s[0]     s[-1]     s[-2]    s[-3]    s[-4]    s[-5]   s[-6]    s[-7]    s[-8]     s[-9]
 291.2129 3.110346 0.9814982 0.8821721 0.9925036 1.001262 1.081113 1.054762 1.07494 1.147514 1.014973 0.9830665
    s[-10]    s[-11]
 0.8696745 0.9165203

  sigma^2:  0.0048

     AIC     AICc      BIC 
5186.587 5188.476 5251.779 
components(fit_es) |>
  autoplot()

Pronostico, intervalo de predicción y análisis de residuales

frcst = fit_es %>% forecast(h = tstng_prds)

fit_es %>%
  forecast(h = tstng_prds) %>%
  autoplot(train)

frcst
frcst %>% hilo(level = c(80, 95))
accuracy(fit_es)
accuracy(fit_lm)
fit_es %>% gg_tsresiduals()

LS0tCnRpdGxlOiAnQW7DoWxpc2lzIGRlIGxvcyBpbmdyZXNvcyBwb3IgcmVtZXNhcyBkZSBNw6l4aWNvJwpzdWJ0aXRsZTogJ0NsYXNlIHNlcmllcyBkZSB0aWVtcG8sIHByaW1hdmVyYSAyMDI0JwphdXRob3I6ICdEYW5pZWwgTnXDsW8sIGRhbmllbC5udW5vQGl0ZXNvLm14JwpkYXRlOiAiRmViIDQsIDIwMjQiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIGhpZ2hsaWdodDogdGFuZ28KICBnaXRodWJfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGV2OiBqcGVnCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBzZXR1cCwgZWNobyA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobz0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA3KQpgYGAKCmBgYHs9aHRtbH0KPHN0eWxlPgouZm9yY2VCcmVhayB7IC13ZWJraXQtY29sdW1uLWJyZWFrLWFmdGVyOiBhbHdheXM7IGJyZWFrLWFmdGVyOiBjb2x1bW47IH0KPC9zdHlsZT4KYGBgCjxjZW50ZXI+IVtdKGh0dHBzOi8vdXBsb2FkLndpa2ltZWRpYS5vcmcvd2lraXBlZGlhL2NvbW1vbnMvZC9kYi9Mb2dvX0lURVNPX25vcm1hbC5qcGcpe3dpZHRoPSIyMCUifTwvY2VudGVyPgoKIyAxKSBEZWZpbmljacOzbgoKIyMgTGEgcHJpbWVyYSBhY3RpdmlkYWQgZGUgY3VhbHF1aWVyIGFuw6FsaXNpcyB5IHByb25vc3RpY28gZXMgZGVmaW5pciBlbCBwcm9ibGVtYQoKRW4gZWwgbGlicm8gRm9yZWNhc3Rpbmc6IFByaW5jaXBsZXMgYW5kIFByYWN0aWNlLCBjYXDDrXR1bG8gcHJpbWVybywgZGVzY3JpYmUgcXVlIGVzIHVuIHByb27Ds3N0aWNvLCBxdWUgc2UgcHVlZGUgcHJvbm9zdGljYXIgeSBjw7NtbyBkZWZpbmlyIGVsIHByb2JsZW1hLCB5IGFsZ3Vub3MgY2Fzb3MgZGUgZXN0dWRpb3MuCgpMZWUgbcOhcyBlbiBlbCBjYXDDrXR1bG8gPGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL2RldGVybWluaW5nLXdoYXQtdG8tZm9yZWNhc3QuaHRtbD4KCiMjIERlc2NyaXBjacOzbiBkZWwgbGFzIHJlbWVzYXMKCkVzIGVsIGVudsOtbyBkZSBkaW5lcm8gZGUgYXF1ZWxsYXMgcGVyc29uYXMgcXVlIHJhZGljYW4gZW4gb3RyYSBuYWNpw7NuIGEgc3UgcGHDrXMgZGUgb3JpZ2VuLiBQb3IgZWplbXBsbywgbG9zIGVudsOtb3MgZGUgZGluZXJvIHF1ZSByZWFsaXphbiBsb3MgbWV4aWNhbm9zIHF1ZSByYWRpY2FuIGVuIEVzdGFkb3MgVW5pZG9zIHkgQ2FuYWTDoSBhIHN1cyBmYW1pbGlhcyBxdWUgdml2ZW4gZW4gTcOpeGljby4KCkNvbiBlbCBzdXJnaW1pZW50byBkZSBudWV2YXMgaGVycmFtaWVudGFzIHRlY25vbMOzZ2ljYXMgZXMgbcOhcyBzZW5jaWxsbyByZWFsaXphciBlc3RhIHRyYW5zYWNjacOzbiB5IGVsIG7Dum1lcm8gZGUgdXN1YXJpb3MgcXVlIGxhIGhhY2VuIHNlIGhhIGluY3JlbWVudGFkbyBlbiBsb3Mgw7psdGltb3MgYcOxb3MuCgpUYW1iacOpbiBleGlzdGVuIGxhcyByZW1lc2FzIGVudmlhZGFzIHBvciBwZXJzb25hcyBxdWUgc2UgZGVkaWNhbiBhIGxvcyBuZWdvY2lvcyB5IHRpZW5lbiBxdWUgcGFnYXIgc3VlbGRvcyBhIGxvcyBlbXBsZWFkb3MgY29udHJhdGFkb3MgZW4gZWwgZXh0ZXJpb3IsIHBvciBlamVtcGxvLiBPdHJhIHRyYW5zZmVyZW5jaWEgbXV5IGNvbcO6biBlcyBwYXJhIGVzdHVkaWFudGVzIHF1ZSBubyBwdWVkZW4gdHJhYmFqYXIgeSBuZWNlc2l0YW4gbWFudGVuZXJzZSBlbiBvdHJvIHBhw61zIG1pZW50cmFzIGVzdHVkaWFuLgoKTGFzIHJlbWVzYXMgc29uIGxhIHNlZ3VuZGEgZnVlbnRlIGRlIGRpdmlzYXMgbcOhcyBpbXBvcnRhbnRlIGRlIE3DqXhpY28gZGVzcHXDqXMgZGUgbG9zIGluZ3Jlc29zIHBvciBwZXRyw7NsZW8uIFJlcHJlc2VudGFuIGNlcmNhIGRlIDMlIGRlbCBQSUIsIDUwJSBkZSBsYXMgZXhwb3J0YWNpb25lcyBwZXRyb2xlcmFzLCAxMzUlIGRlIGxhIGludmVyc2nDs24gZXh0cmFuamVyYSBkaXJlY3RhIHkgMTg5JSBkZSBsb3MgaW5ncmVzb3MgZGUgdmlhamVyb3MgaW50ZXJuYWNpb25hbGVzLgoKTG9zIGluZ3Jlc29zIHBvciByZW1lc2FzIHByb3ZlbmllbnRlcyBkZWwgZXh0ZXJpb3IgYXNjZW5kaWVyb24gYSA0LDc2MCBtaWxsb25lcyBkZSBkw7NsYXJlcyBlbiBkaWNpZW1icmUgZGUgMjAyMSwgbG8gcXVlIGltcGxpY8OzIHVuIGF1bWVudG8gYW51YWwgZGUgMzAuNCUuCgpQb3Igc3UgcGFydGUsIGxhcyByZW1lc2FzIGVudmlhZGFzIHBvciByZXNpZGVudGVzIGVuIE3DqXhpY28gYWwgZXh0ZXJpb3IgbW9zdHJhcm9uIHVuIGNyZWNpbWllbnRvIGFudWFsIGRlIDEuOCUsIGFsIGFsY2FuemFyIHVuIG5pdmVsIGRlIDExNCBtaWxsb25lcyBkZSBkw7NsYXJlcy4KCkNvbiBlc3RvcyByZXN1bHRhZG9zLCBlbCBzdXBlcsOhdml0IGRlIGxhIGN1ZW50YSBkZSByZW1lc2FzIGRlIE3DqXhpY28gY29uIGVsIHJlc3RvIGRlbCBtdW5kbyBmdWUgZGUgNCw2NDYgbWlsbG9uZXMgZGUgZMOzbGFyZXMsIG1heW9yIGFsIGRlIDMsNTM4IG1pbGxvbmVzIGRlIGTDs2xhcmVzIHF1ZSBzZSBwcmVzZW50w7MgZW4gZGljaWVtYnJlIGRlIDIwMjAuCgpDb24gc2VyaWVzIGRlc2VzdGFjaW9uYWxpemFkYXMsIGVuIGVsIMO6bHRpbW8gbWVzIGRlIDIwMjEgbG9zIGluZ3Jlc29zIHkgZWdyZXNvcyBwb3IgcmVtZXNhcyBleGhpYmllcm9uIHJldHJvY2Vzb3MgbWVuc3VhbGVzIGRlIDEuOSB5IDIuMyUsIGVuIGlndWFsIG9yZGVuLiBBc8OtLCBlbiBkaWNpZW1icmUgZGUgMjAyMSBlbCBzdXBlcsOhdml0IGRlIGxhIGN1ZW50YSBkZSByZW1lc2FzIHNlIHNpdHXDsyBlbiA0LDYwMCBtaWxsb25lcyBkZSBkw7NsYXJlcywgcXVlIHNlIGNvbXBhcmEgY29uIGVsIGRlIDQsNjg3IG1pbGxvbmVzIGRlIGTDs2xhcmVzIHF1ZSBzZSBvYnNlcnbDsyBlbiBub3ZpZW1icmUgZGUgMjAyMS4KClBhcmEgbGEgdG90YWxpZGFkIGRlIDIwMjEsIGVsIHZhbG9yIGRlIGxvcyBpbmdyZXNvcyBwb3IgcmVtZXNhcyBmdWUgZGUgNTEsNTk0IG1pbGxvbmVzIGRlIGTDs2xhcmVzLCBtb250byBzdXBlcmlvciBhbCBkZSA0MCw2MDUgbWlsbG9uZXMgZGUgZMOzbGFyZXMgcmVwb3J0YWRvIGVuIDIwMjAgeSBxdWUgc2lnbmlmaWPDsyB1bmEgZXhwYW5zacOzbiBhbnVhbCBkZSAyNy4xJS4KCkR1cmFudGUgMjAyMSwgZWwgOTguOSUgZGVsIHRvdGFsIGRlIGxvcyBpbmdyZXNvcyBwb3IgcmVtZXNhcyBzZSByZWFsaXrDsyBhIHRyYXbDqXMgZGUgdHJhbnNmZXJlbmNpYXMgZWxlY3Ryw7NuaWNhcywgYWwgdWJpY2Fyc2UgZW4gNTEsMDQ1IG1pbGxvbmVzIGRlIGTDs2xhcmVzLiBQb3Igc3UgcGFydGUsIGxhcyByZW1lc2FzIGVmZWN0dWFkYXMgZW4gZWZlY3Rpdm8geSBlc3BlY2llMiB5IGxhcyBtb25leSBvcmRlcnMgcmVwcmVzZW50YXJvbiBlbCAwLjcgeSAwLjQlIGRlbCBtb250byB0b3RhbCwgcmVzcGVjdGl2YW1lbnRlLCBhbCByZWdpc3RyYXIgbml2ZWxlcyBkZSAzMzMgeSAyMTYgbWlsbG9uZXMgZGUgZMOzbGFyZXMsIGVuIGVsIG1pc21vIG9yZGVuLgoKTG9zIGVncmVzb3MgcG9yIHJlbWVzYXMgc3VtYXJvbiAxLDA1NyBtaWxsb25lcyBkZSBkw7NsYXJlcyBlbiAyMDIxLCBjaWZyYSBtYXlvciBhIGxhIGRlIDg5OSBtaWxsb25lcyBkZSBkw7NsYXJlcyBvYnNlcnZhZGEgZW4gMjAyMCB5IHF1ZSBpbXBsaWPDsyB1biBpbmNyZW1lbnRvIGFudWFsIGRlIDE3LjYlLgoKQ29uIGVzdG9zIHJlc3VsdGFkb3MsIGVsIHNhbGRvIHN1cGVyYXZpdGFyaW8gZGUgbGEgY3VlbnRhIGRlIHJlbWVzYXMgZW4gMjAyMSBmdWUgZGUgNTAsNTM3IG1pbGxvbmVzIGRlIGTDs2xhcmVzLCBjaWZyYSBzdXBlcmlvciBhIGxhIGRlIDM5LDcwNiBtaWxsb25lcyBkZSBkw7NsYXJlcyByZXBvcnRhZGEgZW4gMjAyMCB5IHF1ZSByZXByZXNlbnTDsyB1biBhdmFuY2UgYW51YWwgZGUgMjcuMyUuCgojIyMgRGF0b3MKCkJhbmNvIGRlIE3DqXhpY28gcmVnaXN0cmEgbGFzIHJlbWVzYXMgY29tbyBwYXJ0ZSBkZSBsYSBiYWxhbnphIGRlIHBhZ29zIHkgdGllbmUgcmVnaXN0cm8gbWVuc3VhbCBkZXNkZSAxOTk1LgoKTG9zIGRhdG9zIGRlIEJhbnhpY28gcHVlZGVuIHNlciBkZXNjYXJnYWRvcyBlbiBlbCBzaWd1aWVudGUgW2VubGFjZV0oaHR0cHM6Ly93d3cuYmFueGljby5vcmcubXgvU2llSW50ZXJuZXQvY29uc3VsdGFyRGlyZWN0b3Jpb0ludGVybmV0QWN0aW9uLmRvP2FjY2lvbj1jb25zdWx0YXJDdWFkcm8maWRDdWFkcm89Q0U4MSZsb2NhbGU9ZXNjKQoKIyMjIERldGVybWluYXIgcXVlIHByb25vc3RpY2FyCgpTZSBhbmFsaXphcmEgZWwgaW5ncmVzbyB0b3RhbCBkZSByZW1lc2FzLCBjb21vIHNlIG11ZXN0cmEgZW4gbGEgY2FwdHVyYSBzaWd1aWVudGUuCgohW10oLi9pbWFnZXMvYmFueGljby5wbmcpCgojIyMgUG9yIHF1ZSBlcyBpbXBvcnRhbnRlIGFuYWxpemFyIHkgcHJvbm9zdGljYXIgZW4gZWwgY29udGV4dG8gc29jaWFsCgpFbCBmbHVqbyBkZSByZW1lc2FzIGludGVybmFjaW9uYWxlcyB0aWVuZSB1biBlZmVjdG8gaW1wb3J0YW50ZSBzb2JyZSBsYSBhY3RpdmlkYWQgZWNvbsOzbWljYSBlbiBhbGd1bmFzIGVudGlkYWRlcyBxdWUgc2UgbG9jYWxpemFuIGVuIGxhcyByZWdpb25lcyBjZW50cmFsZXMgeSBlbCBzdXIgZGVsIHBhw61zLiBFc3RvIGNvbmR1am8gYSBxdWUgZWwgaW5jcmVtZW50byBvYnNlcnZhZG8gZW4gbGFzIHJlbWVzYXMgaGF5YSBpbmR1Y2lkbyBlbiAyMDE2IHVuIG1heW9yIGNyZWNpbWllbnRvIGVjb27Ds21pY28gZW4gZXN0YXMgcmVnaW9uZXMuCgpFbiBlZmVjdG8sIGNvbW8gcmVzdWx0YWRvIGRlIGxvcyBwYXRyb25lcyBkZSBnYXN0byBkZSBsb3MgaG9nYXJlcywgZGljaGFzIGVudGlkYWRlcyB0aWVuZGVuIGEgc2VyIG3DoXMgcHJvcGVuc2FzIGEgY29uY2VudHJhciBlbCBpbXBhY3RvIGRlIGxhcyByZW1lc2FzIGVuIGxhcyBhY3RpdmlkYWRlcyBjb21lcmNpYWxlcyB5IGxvcyBzZXJ2aWNpb3MuIEFkaWNpb25hbG1lbnRlLCBsb3MgZmx1am9zIGRlIHJlbWVzYXMgY29udHJpYnV5ZW4gYSBzdWF2aXphciBlbCBpbmdyZXNvIGRlIGxvcyBob2dhcmVzIGFudGUgY2hvcXVlcyBzb2JyZSBsYSBhY3RpdmlkYWQgZWNvbsOzbWljYSwgZW4gcGFydGljdWxhciBlbiBlbCBzZWN0b3IgYWdyb3BlY3VhcmlvLgoKKipMYXMgcmVtZXNhcyBzb24gbGEgc2VndW5kYSBmdWVudGUgZGUgZGl2aXNhcyBtw6FzIGltcG9ydGFudGUgZGUgTcOpeGljby4gRW4gbG9zIMO6bHRpbW9zIGHDsW9zIGxhcyByZW1lc2FzIGhhbiBhbGNhbnphZG8gbcOheGltb3MgaGlzdMOzcmljb3MsIGFkZW3DoXMsIGhhbiBvYnRlbmlkbyBtYXlvciBhdGVuY2nDs24gcG9yIGxhcyByZWNpZW50ZXMgaW52ZXN0aWdhY2lvbmVzIHF1ZSBzZcOxYWxhbiBjw7NtbyBjw6FydGVsZXMgZGVsIG5hcmNvIGxhdmFuIGRpbmVybyB2w61hIHJlbWVzYXMuKioKCiMgMikgRGF0b3MKCiMjIEltcG9ydGFyCgpFbiBsYXMgc2lndWllbnRlcyBjZWxkYXMgc2UgbXVlc3RhbiBsb3MgcGFzb3MgcGFyYSBpbXBvcnRhciBsb3MgZGF0b3MgZGVzZGUgbWkgZXNwYWNpbyBkZSB0cmFiYWpvLiBQb3IgcXVlIGVzIHVuIGFyY2hpdm8gZW4gZm9ybWF0byBFeGNlbCwgbGEgbGlicmVyw61hIHVzYWRhIGVzIGByZWFkeGxgLgoKTGVlIG3DoXMgZW4gZWwgbm90ZWJvb2sgaW50cm9kdWN0b3JpbyB2aXN0byBlbiBjbGFzZTogW3RpZHl2ZXJzZV0oaHR0cHM6Ly9pdGVzby5pbnN0cnVjdHVyZS5jb20vY291cnNlcy8zNjY3My9maWxlcy83MDM4Mzg2P21vZHVsZV9pdGVtX2lkPTE1Mjc2NTgmZmRfY29va2llX3NldD0xKSwgc2VjY2nDs24gKkRhdGEgc2NpZW5jZSB3b3JrZmxvdyosIHBhc28gKjEgSW1wb3J0IERhdGEqLgoKVGFtYmnDqW4gZW4gZWwgbGlicm8gW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZGxleS5uei9pbXBvcnQpIGNhcMOtdHVsbyAyMC4KCiFbXSguL2ltYWdlcy9pbXBvcnQlMjB0aWR5dmVyc2UucG5nKQoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQoKI3Jhd19kYXRhID0gcmVhZF94bHN4KHBhdGggPSAiL1VzZXJzL2RhbmllbG51bm8vR2l0SHViL3RpbWVfc2VyaWVzX3MyMDI0L0FuYWxpc2lzL3JlbWVzYXMgYmFueGljby54bHN4Iiwgc2hlZXQgPSAiSG9qYTEiLCBjb2xfbmFtZXMgPSBGQUxTRSkKcmF3X2RhdGEgPSByZWFkX3hsc3gocGF0aCA9ICJ+L0dpdEh1Yi90aW1lX3Nlcmllc19zMjAyNC9BbmFsaXNpcy9yZW1lc2FzL3JlbWVzYXMgYmFueGljby54bHN4Iiwgc2hlZXQgPSAiSG9qYTEiLCBjb2xfbmFtZXMgPSBGQUxTRSkKCmhlYWQocmF3X2RhdGEsIDIwKQpgYGAKCiMjIEVsaW1pbmFyIGZpbGFzCgpMb3MgZGF0b3Mgbm8gaW5pY2lhbiBoYXN0YSBsYSBmaWxhIDE4LiBVc2FuZG8gYGRwbHlyOjpzbGljZWAgc2UgcHVlZGVuIGVsaW1pbmFuIGxhcyBwcmltZXJhcyAxOCBmaWxhcy4KCkxlZSBtw6FzIGVuIGVsIG5vdGVib29rIGludHJvZHVjdG9yaW8gdmlzdG8gZW4gY2xhc2U6IFt0aWR5dmVyc2VdKGh0dHBzOi8vaXRlc28uaW5zdHJ1Y3R1cmUuY29tL2NvdXJzZXMvMzY2NzMvZmlsZXMvNzAzODM4Nj9tb2R1bGVfaXRlbV9pZD0xNTI3NjU4JmZkX2Nvb2tpZV9zZXQ9MSksIHNlY2Npw7NuICpEYXRhIHNjaWVuY2Ugd29ya2Zsb3cqLCBwYXNvICozIHVuZGVyc3RhbmQgeW91ciBkYXRhKi4KCiFbXSguL2ltYWdlcy9kcGx5ci5wbmcpCgpUYW1iacOpbiBlbiBlbCBsaWJybyBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkbGV5Lm56L2ltcG9ydCkgY2Fww610dWxvIDI3LgoKYGBge3J9CiNyYXdfZGF0YSA9IHJhd19kYXRhWy0oMToxOCksIF0KZGF0YSA9IGRwbHlyOjpzbGljZShyYXdfZGF0YSwgLSgxOjE4KSkKaGVhZChkYXRhLCAyKQpgYGAKCiMjIFJlbm9tYnJhIGxhcyBjb2x1bW5hcwoKTGFzIGRpbWVuc2lvbmVzIGVzdMOhbiBub21icmFkYXMgY29tbyAqLi4uMSogeSAqLi4uMiosIHBvciBsbyB0YW50byBlcyBjb252ZW5pZW50ZSByZW5vbWJyYXJsYXMgc2luIGVzcGFjaW9zIG5pIGNhcmFjdGVyZXMgZXNwZWNpYWxlcy4KCmBgYHtyfQpjb2xuYW1lcyhkYXRhKSA9IGMoJ2RhdGUnLCAndmFsdWUnKQpoZWFkKGRhdGEsIDIpCmBgYAoKIyMgQ2FtYmlhIGVsIHRpcG8gZGUgZGF0byBkZSBsYXMgY29sdW1uYXMKCkxhcyBjb2x1bW5hcyBzb24gYGNocmAgcGVybyBkYXRlIHRpZW5lIHF1ZSBzZXIgdW5hIGZlY2hhIG1lbnN1YWwgeSB2YWx1ZSBzb24gbWlsbG9uZXMgZGUgZMOzbGFyZXMuIFV0aWxpemEgYGx1YnJpZGF0ZWAgcGFyYSBoYWNlciBsYSB0cmFuc2Zvcm1hY2nDs24gZGUgbGEgZmVjaGEgbyBSIGLDoXNpY28uCgpMZWUgbcOhcyBlbiBlbCBub3RlYm9vayBpbnRyb2R1Y3RvcmlvIHZpc3RvIGVuIGNsYXNlOiBbdGlkeXZlcnNlXShodHRwczovL2l0ZXNvLmluc3RydWN0dXJlLmNvbS9jb3Vyc2VzLzM2NjczL2ZpbGVzLzcwMzgzODY/bW9kdWxlX2l0ZW1faWQ9MTUyNzY1OCZmZF9jb29raWVfc2V0PTEpLCBzZWNjacOzbiAqRGF0YSBzY2llbmNlIHdvcmtmbG93KiwgcGFzbyAqMyB1bmRlcnN0YW5kIHlvdXIgZGF0YTogZGF0YSB0cmFuc2Zvcm1hdGlvbiouCgpUYW1iacOpbiBlbiBlbCBsaWJybyBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkbGV5Lm56L2RhdGV0aW1lcykgY2Fww610dWxvIDE3LgoKIyMjIFRyYW5zZm9ybWEgZWwgdGlwbyB0ZXh0byBjw7NtbyBudW3DqXJpY28KCmBgYHtyfQpkYXRhJGRhdGUgPSBhcy5udW1lcmljKGRhdGEkZGF0ZSkKYGBgCgojIyMgVHJhbnNmb3JtYSBlbCBuw7ptZXJvIGEgZmVjaGEgdXRpbGl6YW5kbyBlbCBhcmd1bWVudG8gb3JpZ2luCgpFbCBuw7ptZXJvIHJlcHJlc2VudGEgdW4gZMOtYSBlbiBsYSBzZXJpZSBxdWUgaW5pY2lhIGVsIHByaW1lcm8gZGUgZW5lcm8gZGUgMTk5MC4gUG9yIGxvIHRhbnRvIHV0aWxpemFtb3MgZWwgYXJndW1lbnRvIG9yaWdpbiBxdWUgaW5kaWNhIGVsIGluaWNpbyBkZSBsYSBzZXJpZS4KCmBgYHtyfQpkYXRhJGRhdGUgPSBhcy5EYXRlKGRhdGEkZGF0ZSwgb3JpZ2luID0gIjE4OTktMTItMzAiKQpgYGAKCiMjIyBDcmVhIHVuYSBzZWN1ZW5jw61hIGV4cGxpY2l0YSBsYSBwZXJpb2RpY2lkYWQgbWVuc3VhbAoKYGBge3J9CmRhdGEkZGF0ZSA9IHNlcShmcm9tID0gbWluKGRhdGEkZGF0ZSksIHRvID0gbWF4KGRhdGEkZGF0ZSksIGJ5ID0gIjEgbW9udGgiKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeSh0c2liYmxlKQpkYXRhJGRhdGUgPSB5ZWFybW9udGgoZGF0YSRkYXRlKQpoZWFkKGRhdGEsIDIpCmBgYAoKIyMjIENhbWJpYSBlbCB0aXBvIGRlIGRhdG8gZGUgbGEgY29sdW1uYSB2YWx1ZQoKYGBge3J9CmRhdGEkdmFsdWUgPSBhcy5udW1lcmljKGRhdGEkdmFsdWUpCmhlYWQoZGF0YSwgMikKYGBgCgojIyBDcmVhIHVuIHRzaWJibGUKClVzYW5kbyBgdHNpYmJsZTo6YXNfdHNpYmJsZSgpYCBhc2lnbmEgbGEgZmVjaGEgY29tbyBpbmRleC4gRW4gZXN0ZSBjYXNvIGBrZXlgIG5vIGVzIG5lY2VzYXJpbyBwZXJvIGRlY2lkZSBzaSBlcyBpbXBvcnRhbnRlIHBhcmEgdHUgc2VyaWUuCgpMZWUgbcOhcyBlbiBlbCBub3RlYm9vayBpbnRyb2R1Y3RvcmlvIHZpc3RvIGVuIGNsYXNlOiBbdGlkeXZlcnNlXShodHRwczovL2l0ZXNvLmluc3RydWN0dXJlLmNvbS9jb3Vyc2VzLzM2NjczL2ZpbGVzLzcwMzgzODY/bW9kdWxlX2l0ZW1faWQ9MTUyNzY1OCZmZF9jb29raWVfc2V0PTEpLCBzZWNjacOzbiAqRGF0YSBzY2llbmNlIHdvcmtmbG93KiwgcGFzbyAqMiBUaWR5IERhdGEqLiBZIGVsIGVqZW1wbG8gMS4KClRhbWJpw6luIGxhIGRvY3VtZW50YWNpw7NuIFt0c2liYmxlXShodHRwczovL3RzaWJibGUudGlkeXZlcnRzLm9yZy8pIHkgZWwgbGlicm8gW0ZvcmVjYXN0OiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvdHNpYmJsZXMuaHRtbCkgY2Fww610dWxvIDIuMQoKYGBge3J9CmRhdGEgPSBhc190c2liYmxlKGRhdGEsIGluZGV4PWRhdGUsIHJlZ3VsYXI9VFJVRSkKaGVhZChkYXRhLCAyKQpgYGAKCmBgYHtyfQppbnRlcnZhbChkYXRhKQpgYGAKCiMjIyBWYWxvcmVzIGZhbHRhbnRlcyB5IGZhY3RvcmVzCklkZW50aWZpY2Egc2kgdHUgc2VyaWUgZGUgdGllbXBvIHRpZW5lIHZhbG9yZXMgZmFsdGFudGVzIG8gZmFjdG9yZXMgeSBoYXMgbGFzIHRyYW5zZm9ybWFjaW9uZXMgY29ycmVzcG9uZGllbnRlcyBjb24gYXl1ZGEgZGVsIGxpYnJvIFtSIGZvciBkYXRhIHNjaWVuY2UgLSBUcmFuc2Zvcm1dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovdHJhbnNmb3JtKS4KCgojIDMpIEFuw6FsaXNpcwoKIyMgVmlzdWFsaXphY2nDs24gZGUgbGEgc2VyaWUKClV0aWxpemEgZ2dwbG90MiBwYXJhIGdyw6FmaWNhciBsYSBzZXJpZQoKTGVlIG3DoXMgZW4gZWwgbm90ZWJvb2sgaW50cm9kdWN0b3JpbyB2aXN0byBlbiBjbGFzZTogW3RpZHl2ZXJzZV0oaHR0cHM6Ly9pdGVzby5pbnN0cnVjdHVyZS5jb20vY291cnNlcy8zNjY3My9maWxlcy83MDM4Mzg2P21vZHVsZV9pdGVtX2lkPTE1Mjc2NTgmZmRfY29va2llX3NldD0xKSwgc2VjY2nDs24gKkRhdGEgc2NpZW5jZSB3b3JrZmxvdyosIHBhc28gKjMgVW5kZXJzdGFuZCB5b3VyIGRhdGEgLSB2aXN1YWxpemF0aW9uKi4KClZlIGxvcyBlamVtcGxvcyB5IGV4cGxpY2FjaW9uZXMgdmlzdGFzIGVuIGNsYXNlOiBbZ2dwbG90Ml0oaHR0cHM6Ly9pdGVzby5pbnN0cnVjdHVyZS5jb20vY291cnNlcy8zNjY3My9maWxlcy83MDE0NTAxP21vZHVsZV9pdGVtX2lkPTE1MjU0MzAmZmRfY29va2llX3NldD0xKSwgc2VjY2nDs24gKkdyw6FmaWNhcyBjb24gZ2dwbG90MiouCgohW10oLi9pbWFnZXMvZ2dwbG90Mm5iLnBuZykKClRhbWJpw6luIGVsIGxpYnJvIFtGb3JlY2FzdDogUHJpbmNpcGxlcyBhbmQgUHJhY3RpY2VdKGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL3RpbWUtcGxvdHMuaHRtbCkgY2Fww610dWxvIDIuMgoKIVtdKC4vaW1hZ2VzL2NhcDIxLnBuZykKCiMjIyBUaW1lIHBsb3QKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZmVhc3RzKQpmZWFzdHM6OmF1dG9wbG90KGRhdGEpICsgZ2d0aXRsZSgnUmVtZXNhcyBwb3IgbWVzJykgKyB5bGFiKCdNaWxsb25lcyBkZSBkw7NsYXJlcycpICsgeGxhYignRmVjaGEnKQpgYGAKCgojIyMgUGF0cm9uZXMKCkdyw6FmaWNhbWVudGUgcHJlc2VudGEgKip0ZW5kZW5jaWEqKiwgKiplc3RhY2lvbmFsaWRhZCoqIHkgaGV0ZXJvc2NlZGFzdGljaWRhZC4gUGFyZWNlIHByZXNlbnRhciAqKmNpY2xvcyoqIHJlbGFjaW9uYWRvcyBhIGxhIGFjdGl2aWRhZCBlY29uw7NtaWNhLgoKQ29uc3VsdGEgZWwgbGlicm8gW0ZvcmVjYXN0OiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvdHNwYXR0ZXJucy5odG1sKSBjYXDDrXR1bG8gMi4zCgojIyMgR3LDoWZpY2EgZXN0YWNpb25hbGVzCgpVbmEgZ3LDoWZpY2EgZXN0YWNpb25hbCBlcyBsbyBtaXNtbyBxdWUgdW4gdGltZSBwbG90IHBlcm8gZ3JhZmljYWRhIHVzYW5kbyBsb3MgcGVyaW9kb3MgZXN0YWNpb25hbGVzLiBFbiBlc3RlIGNhc28gbGEgZXN0YWNpb25hbGlkYWQgZXMgbWVuc3VhbC4KCkNvbnN1bHRhIGVsIGxpYnJvIFtGb3JlY2FzdDogUHJpbmNpcGxlcyBhbmQgUHJhY3RpY2VdKGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL3NlYXNvbmFsLXBsb3RzLmh0bWwpIGNhcMOtdHVsbyAyLjQKCmBgYHtyfQpkYXRhICU+JSBnZ19zZWFzb24odmFsdWUsIGxhYmVscyA9ICJib3RoIikgKwogICAgZ2d0aXRsZSgnUmVtZXNhcyBwb3IgYcOxbycpICsgeWxhYignTWlsbG9uZXMgZGUgZMOzbGFyZXMnKSArIHhsYWIoJ01lcycpCmBgYAoKVXRpbGl6YSBgcGxvdGx5OjpnZ3Bsb3RseSgpYCBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zCgpgYGB7cn0Kc3VwcHJlc3NXYXJuaW5ncyhsaWJyYXJ5KHBsb3RseSkpCgp5ZWFybHlfZGF0YV9wbG90ID0gZGF0YSAlPiUgZ2dfc2Vhc29uKHZhbHVlLCBsYWJlbHMgPSAiYm90aCIpICsKICAgIGdndGl0bGUoJ1JlbWVzYXMgcG9yIGHDsW8nKSArIHlsYWIoJ01pbGxvbmVzIGRlIGTDs2xhcmVzJykgKyB4bGFiKCdNZXMnKQoKZ2dwbG90bHkoeWVhcmx5X2RhdGFfcGxvdCkKYGBgCgojIyMgU3ViIGdyw6FmaWNhcyBlc3RhY2lvbmFsZXMKClVuIGdyw6FmaWNvIGFsdGVybmF0aXZvIHF1ZSBlbmZhdGl6YSBsb3MgcGF0cm9uZXMgZXN0YWNpb25hbGVzIGVzIGFxdWVsIGVuIGVsIHF1ZSBsb3MgZGF0b3MgZGUgY2FkYSB0ZW1wb3JhZGEgc2UgcmVjb3BpbGFuIGVuIG1pbmlncsOhZmljb3MgZGUgdGllbXBvIHNlcGFyYWRvcy4KCkNvbnN1bHRhIGVsIGxpYnJvIFtGb3JlY2FzdDogUHJpbmNpcGxlcyBhbmQgUHJhY3RpY2VdKGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL3N1YnNlcmllcy5odG1sKSBjYXDDrXR1bG8gMi41CgpgYGB7cn0Kc3Vic2VyaWVzX3Bsb3QgPSBkYXRhICU+JSBnZ19zdWJzZXJpZXModmFsdWUpCmdncGxvdGx5KHN1YnNlcmllc19wbG90KQpgYGAKCiMjIyBHcsOhZmljbyBkZSByZXphZ29zCgpFc3RvcyBncsOhZmljb3Mgc29uIGdyw6FmaWNvcyBkZSBkaXNwZXJzacOzbiBldmFsdWFkb3MgZW4gbG9zIHJlemFnb3MgZGUgbGEgbWlzbWEgc2VyaWUgcXVlIG1pZGVuIGxhIGNvcnJlbGFjacOzbiBlbnRyZSBwZXJpb2Rvcy4gRW4gb3RyYXMgcGFsYWJyYXMsIHBhcmEgZXN0YSBzZXJpZSBkZSB0aWVtcG8gbWVuc3VhbCwgbWlkZSBsYSByZWxhY2nDs24gZGUgdW4gbWVzIGVuIGVsIHRpZW1wbyBjb250cmEgdW4gbWVzIChuIGxhZ3MpIGRlbCBwYXNhZG8uCgpDb25zdWx0YSBlbCBsaWJybyBbRm9yZWNhc3Q6IFByaW5jaXBsZXMgYW5kIFByYWN0aWNlXShodHRwczovL290ZXh0cy5jb20vZnBwMy9sYWctcGxvdHMuaHRtbCkgY2Fww610dWxvIDIuNwoKYGBge3J9CmxhZ3NfcGxvdHMgPSBkYXRhICU+JSBmaWx0ZXIoeWVhcihkYXRlKSA+IDIwMTgpICU+JSBnZ19sYWcodmFsdWUsIGdlb20gPSAicG9pbnQiLCBsYWdzID0gMToxMikgKyBsYWJzKHggPSAibGFnKFJlbWVzYSwgaykiKQoKc3VwcHJlc3NXYXJuaW5ncyhnZ3Bsb3RseShsYWdzX3Bsb3RzKSkKYGBgCgojIyMgQXV0b2NvcnJlbGFjacOzbgoKQXPDrSBjb21vIGxhIGNvcnJlbGFjacOzbiBtaWRlIGVsIGFsY2FuY2UgZGUgdW5hIHJlbGFjacOzbiBsaW5lYWwgZW50cmUgZG9zIHZhcmlhYmxlcywgbGEgYXV0b2NvcnJlbGFjacOzbiBtaWRlIGxhIHJlbGFjacOzbiBsaW5lYWwgZW50cmUgdmFsb3JlcyByZXphZ2Fkb3MgZGUgdW5hIHNlcmllIHRlbXBvcmFsLgoKQ29uc3VsdGEgZWwgbGlicm8gW0ZvcmVjYXN0OiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZV0oaGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL2FjZi5odG1sKSBjYXDDrXR1bG8gMi44CgpgYGB7cn0KZGF0YSAlPiUgQUNGKHZhbHVlLCBsYWdfbWF4ID0gMTIpCmBgYAoKYGBge3J9CmRhdGEgJT4lIEFDRih2YWx1ZSwgbGFnX21heCA9IDI0KSAlPiUgYXV0b3Bsb3QoKSArIGxhYnModGl0bGU9J1JlbWVzYXMgcG9yIGHDsW8nKQpgYGAKCkN1YW5kbyBsb3MgZGF0b3MgdGllbmVuICoqdGVuZGVuY2lhKiosIGxhcyBhdXRvY29ycmVsYWNpb25lcyBwYXJhIGRlc2Zhc2VzIHBlcXVlw7FvcyB0aWVuZGVuIGEgc2VyIGdyYW5kZXMgeSBwb3NpdGl2YXMgcG9ycXVlIGxhcyBvYnNlcnZhY2lvbmVzIGNlcmNhbmFzIGVuIGVsIHRpZW1wbyB0YW1iacOpbiBsbyBzb24gZW4gdmFsb3IuIFBvciBsbyB0YW50bywgZWwgQUNGIGRlIHVuYSBzZXJpZSB0ZW1wb3JhbCBjb24gdGVuZGVuY2lhIHRpZW5kZSBhIHRlbmVyIHZhbG9yZXMgcG9zaXRpdm9zIHF1ZSBkaXNtaW51eWVuIGxlbnRhbWVudGUgYSBtZWRpZGEgcXVlIGF1bWVudGFuIGxvcyByZXphZ29zLgoKQ3VhbmRvIGxvcyBkYXRvcyBzb24gKiplc3RhY2lvbmFsZXMqKiwgbGFzIGF1dG9jb3JyZWxhY2lvbmVzIHNlcsOhbiBtYXlvcmVzIHBhcmEgbG9zIHJlemFnb3MgZXN0YWNpb25hbGVzIChlbiBtw7psdGlwbG9zIGRlbCBwZXLDrW9kbyBlc3RhY2lvbmFsKSBxdWUgcGFyYSBvdHJvcyByZXphZ29zLgoKU2VyYSBpbXBvcnRhbnRlIHJlYWxpemFyIGxhcyBtaXNtYXMgZ3LDoWZpY2FzIHVuYSB2ZXogcXVlIGxhIHNlcmllIHNlYSBlc3RhY2lvbmFsLgoKQWRlbcOhcyBkZSBsbyBkZXNjcml0byBlbiBsYSBwcmltZXJhIHBhcnRlIGRlbCBkb2N1bWVudG8sIGNvbiBsYXMgZ3LDoWZpY2FzIHBvZGVtb3Mgb2JzZXJ2YXIgbG8gc2lndWllbnRlOgoKR3LDoWZpY2FtZW50ZSBwcmVzZW50YSAqKnRlbmRlbmNpYSoqLCAqKmVzdGFjaW9uYWxpZGFkKiouIFBhcmVjZSBwcmVzZW50YXIgKipjaWNsb3MqKiByZWxhY2lvbmFkb3MgYSBsYSBhY3RpdmlkYWQgZWNvbsOzbWljYS4KCmVzIGludGVyZXNhbnRlIG9ic2VydmFyIGNvbiBlc3RlIGdyw6FmaWNvIHF1ZSBhIHBhcnRpciBkZWwgYcOxbyAyMDA0IGxvcyBkYXRvcyBzZSB2ZW4gc2VwYXJhZG9zLCBlcyBkZWNpciwgbGEgYW1wbGl0dWQgZGUgbG9zIGRhdG9zIHNlIHZ1ZWx2ZSBtw6FzIGdyYW5kZSBlbiBjYWRhIGHDsW8gZGVzcHXDqXMgcXVlIHBhc2EuIFBvZGVtb3MgY29uY2x1aXIgdHJlcyBjb3NhcyBjb24gZXN0bzoKCi0gTG9zIHByaW1lcm9zIHNpZXRlIHUgb2NobyBhw7FvcyBkZSBsYSBzZXJpZSBoYXkgY3JlY2ltaWVudG8sIHBlcm8gYSBwYXJ0aXIgZGVsIDIwMDMgZWwgY3JlY2ltaWVudG8gZXMgZXhwb25lbmNpYWwgZGUgY8OzbW8gZXJhIGFudGVzLgotIEVuIGVzYXMgZmVjaGFzIHNlIHZvbHZpw7MgbcOhcyBmw6FjaWwgaGFjZXIgdHJhbnNmZXJlbmNpYXMgZWxlY3Ryw7NuaWNhcyB5IHBvciBsbyB0YW50byBjb250YWJpbGl6YXJsbyBlbiBsYSBiYWxhbnphIGRlIHBhZ29zLgotIEVsIGNyZWNpbWllbnRvIHB1ZWRlIHNlciB0YW1iacOpbiBwb3IgbGEgc29saWRhcmlkYWQgZGUgbG9zIG1pZ3JhbnRlcy4KCj5FbiBlbCAyMDAxLCBlbiByZXNwdWVzdGEgYSBsYSAiU29jaWVkYWQgcGFyYSBsYSBwcm9zcGVyaWRhZCIgZW50cmUgTcOpeGljbyB5IEVzdGFkb3MgVW5pZG9zLCBlbCBCYW5jbyBkZSBNw6l4aWNvIHkgbG9zIEJhbmNvcyBkZSBsYSBSZXNlcnZhIEZlZGVyYWwgZGUgbG9zIEVzdGFkb3MgVW5pZG9zIGFjb3JkYXJvbiBlc3R1ZGlhciBsYSBwb3NpYmlsaWRhZCBkZSBpbnRlcmNvbmVjdGFyIHN1cyBzaXN0ZW1hcyBkZSBwYWdvcy4gTGEgaW50ZXJjb25leGnDs24gaGEgcHJvdmlzdG8gYSBudWVzdHJhcyBlY29ub23DrWFzIGRlIHVuIG1lY2FuaXNtbyBlZmljaWVudGUgcGFyYSBpbnRlcmNhbWJpYXIgcGFnb3MgZW50cmUgY3VlbnRhcyBiYW5jYXJpYXMgZGUgYW1ib3MgcGHDrXNlcy4KCj5FbiBvY3R1YnJlIGRlIDIwMDMsIGxvcyBCYW5jb3MgZGUgbGEgUmVzZXJ2YSBGZWRlcmFsIGRlIGxvcyBFc3RhZG9zIFVuaWRvcyB5IGVsIEJhbmNvIGRlIE3DqXhpY28gY29uZWN0YXJvbiBzdXMgc2lzdGVtYXMgZGUgcGFnbyBwYXJhIGVsIGVudsOtbyBkZSBsb3MgcGFnb3MgZGUgbG9zIHBlbnNpb25hZG9zIGRlbCBHb2JpZXJubyBkZSBsb3MgRXN0YWRvcyBVbmlkb3MgcXVlIHJhZGljYW4gZW4gTcOpeGljby4KCj5Qb3N0ZXJpb3JtZW50ZSwgZGVzZGUgZWwgMiBkZSBmZWJyZXJvIGRlIDIwMDQsIGxvcyB1c3VhcmlvcyBkZSBsYXMgaW5zdGl0dWNpb25lcyBmaW5hbmNpZXJhcyBkZSBsb3MgRXN0YWRvcyBVbmlkb3Mgc3VzY3JpdGFzIGEgRGlyZWN0byBhIE3DqXhpY28gcHVlZGVuIGVudmlhciBwYWdvcyBhIGN1YWxxdWllciBjdWVudGEgYmFuY2FyaWEgZW4gTcOpeGljby4KCj5EZXNkZSBqdWxpbyBkZSAyMDA1LCBlc3RlIHNlcnZpY2lvIGRlIHBhZ29zIHNlIHJlZ2lzdHLDsyBjb24gZWwgbm9tYnJlIERpcmVjdG8gYSBNw6l4aWNvCgpFbnRvbmNlcyBwYXJhIGNvbnNpZGVyYXIgY29tbyBwZXJpb2RvcyBxdWUgaGFuIGFmZWN0YWRvIGxhIHNlcmllIGRlIHRpZW1wbyB0ZW5lbW9zOgoKLSAyMDAzLTEwIGZhY2lsaWRhZCBkZSB0cmFuc2ZlcmVuY2lhcyBlbGVjdHLDs25pY2FzCi0gMjAwNy0xMiwgMjAwOS0wNiByZWNlc2nDs24gZ2xvYmFsIHBvciBjcmlzaXMgaGlwb3RlY2FyaWEKLSAyMDIwLTAyLCAyMDIwLTA0IHJlY2VzacOzbiBnbG9iYWwgcG9yIENPVklELTE5Ci0gQXBhcnRpciBkZWwgMjAxOCBoYXkgdW4gY2FtYmlvIGRlIHRlbmRlbmNpYSB5IGVsIGNyZWNpbWlldG8gZXMgYWNlbGVyYWRvLgoKQ29uIGxvcyBncsOhZmljb3MgZGUgcmV6YWdvcyBwb2RlbW9zIGFwcmVjaWFyIHF1ZSBtdWVzdHJhIG1heW9yIGNvcnJlbGFjacOzbiBjb24gZWwgcGVyaW9kbyBpbm1lZGlhdG8gYW50ZXJpb3IgKDEpIHkgZWwgbWlzbW8gcGVyaW9kbyBkZWwgYcOxbyBwYXNhZG8gKDEyKS4gRXMgZGVjaXIsIHNlIGVzcGVyYSBxdWUgc2UgZW52acOpIHVuYSBzaW1pbGFyIGNhbnRpZGFkIGRlIGRpbmVybyBjb21wYXJhZGEgYSBlc3RvcyBkb3MgcGVyaW9kb3MgZGVsIHBhc2FkbyAoMSB5IDEyKSwgcGVybyBwYXJhIGVsIHBlcmlvZG8gMTIgc2UgZXNwZXJhIHF1ZSBzZSBlbnZpw6kgbcOhcyBkaW5lcm8uCgojIyBFc3RhZMOtc3RpY2EgZGVzY3JpcHRpdmEKIyMjIE1lZGlkYXMgZGUgdGVuZGVuY2lhIGNlbnRyYWwKCkNhbGN1bGEgbGEgbWVkaWEsIG1vZGEgeSBtZWRpYW5hLiBFc3RvcyBlc3RhZGlzdGljb3MgcHVlZGVuIHNlciBkZSBsYSB0b3RhbGlkYWQgZGUgbGEgc2VyaWUgbyBwb3IgYcOxbyBvIHBvciBwZXJpb2RvIGVzdGFjaW9uYWwuCgpgYGB7cn0KcHJpbnQocGFzdGUoJ2ZlY2hhIGluaWNpYWwnLCBtaW4oZGF0YSRkYXRlKSkpCnByaW50KHBhc3RlKCdmZWNoYSBmaW5hbCcsIG1heChkYXRhJGRhdGUpKSkKcHJpbnQocGFzdGUoJ29ic2VydmFjaW9uZXMnLCBucm93KGRhdGEpKSkKcHJpbnQocGFzdGUoJ2V4aXN0ZW4nLCBzdW0oaXMubmEoZGF0YSkpLCAnZGF0b3MgZmFsdGFudGVzJykpCmBgYAoKYGBge3J9CnN1bW1hcnkoZGF0YVssICd2YWx1ZSddKQpgYGAKCk9ic2VydmFtb3MgcXVlLCBwb3IgbWVzLCBlbCBwcm9tZWRpbyBkZSBtaWxsb25lcyBkZSBkw7NsYXJlcyBlcyBkZSAkMSw5OTMuMiBtaWxsb25lcyBkZSBkw7NsYXJlcywgZWwgbcOtbmltbyBmdWUgZGUgJDI0OC4xIG1pbGxvbmVzIGRlIGTDs2xhcmVzLCBtaWVudHJhcyBxdWUgZWwgbcOheGltbyBmdWUgZGUgJDUsODE3LjggbWlsbG9uZXMuCgpgYGB7cn0KYm94cGxvdCA9IGRhdGEgJT4lIAogICAgICAgICAgICBtdXRhdGUoeWVhciA9IHllYXIoZGF0ZSkpICU+JSAKICAgICAgICAgICAgZ2dwbG90KGFlcyh4ID0gYXMuZmFjdG9yKHllYXIpLCB5ID0gdmFsdWUpKSArIAogICAgICAgICAgICBnZW9tX2JveHBsb3QoKSArIAogICAgICAgICAgICB4bGFiKCdBw7FvJykgKyAKICAgICAgICAgICAgeWxhYignUmVtZXNhcycpCgpnZ3Bsb3RseShib3hwbG90KQpgYGAKCiMjIyBNZWRpZGFzIGRlIGRpc3BlcnNpw7NuCgpFc3RvcyBlc3RhZMOtc3RpY29zIHF1ZSBub3MgZGFuIGluZm9ybWFjacOzbiBhbCByZXNwZWN0byBkZSBsYSB2YXJpYWJpbGlkYWQgbyBzZXBhcmFjacOzbiBkZSBsb3MgZGF0b3MgZ2VuZXJhbG1lbnRlIHJlc3BlY3RvIGEgbGEgbWVkaWEuIE5vcyBheXVkYW4gYSBjb21wcmVuZGVyIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zIHkgZXZpdGFuIHRvbWFyIGNvbmNsdXNpb25lcyBlcnLDs25lYXMgYWwgY29tcGFyYXIgZGlzdGludG9zIGdydXBvcy4KCmBgYHtyfQpzZChkYXRhJHZhbHVlKQpgYGAKCmBgYHtyfQp2YXIoZGF0YSR2YWx1ZSkKYGBgCgpgYGB7cn0KbGlicmFyeShFbnZTdGF0cykKa3VydG9zaXMoZGF0YSR2YWx1ZSkKc2tld25lc3MoZGF0YSR2YWx1ZSkKc2hhcGlyby50ZXN0KGRhdGEkdmFsdWUpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dFeHRyYSkKcCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHg9ZGF0ZSwgeT12YWx1ZSkpICsgCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0xMDAwKSArIAogICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9MzAwMCkgKwogICAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICAgIGdndGl0bGUoJ1JlbWVzYXMgcG9yIG1lcycpICsgeWxhYignTWlsbG9uZXMgZGUgZMOzbGFyZXMnKSArIHhsYWIoJ0ZlY2hhJykKCmdnTWFyZ2luYWwocCwgdHlwZT0naGlzdG9ncmFtJywgbWFyZ2lucyA9ICd5JykKYGBgCgpgYGB7cn0KaGlzdG9ncmFtID0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gdmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oIGJpbnMgPSAyMCwgZmlsbCA9ICJibGFjayIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbWEiLAogICAgICAgeCA9ICJWYWx1ZSIsCiAgICAgICB5ID0gIkRlbnNpZGFkIikKCmdncGxvdGx5KGhpc3RvZ3JhbSkKYGBgCgpMYSBkaXN0cmlidWNpw7NuIHByZXNlbnRhIHVuIHNlc2dvIGRlIHNpbWV0csOtYSBuZWdhdGl2byBhIGxhIGl6cXVpZXJkYSB5IGF1bnF1ZSBsb3MgdmFsb3JlcyBubyBzb24gdGFuIGdyYW5kZXMsIGFsZ3VuYSB0cmFuc2Zvcm1hY2nDs24gc2Vyw61hIMO6dGlsIGFudGVzIGRlIGhhY2VyIGxhIGRlc2NvbXBvc2ljacOzbiBkZSBsYSBzZXJpZSB5IGxvcyBwcm9uw7NzdGljb3MuCgojIyMgVmFsb3JlcyBhdMOtcGljb3MKCkxhIGRldGVjY2nDs24gZGUgb3V0bGllcnMgZXMgaW1wb3J0YW50ZSBwYXJhIGFmaW5hciBudWVzdHJvIHByb27Ds3N0aWNvIHkgZWxpbWluYXIgbGFzIG9ic2VydmFjaW9uZXMgYXTDrXBpY2FzLgpFeGlzdGVuIGRpZmVyZW50ZXMgZm9ybWFzIGRlIGRlZmluaXIgdmFsb3JlcyBhdMOtcGljb3MuIEVuIGVzdGUgY2Fzbywgc2kgbG9zIG91dGxpZXJzIHNvbiAxLjUgdmVjZXMgZWwgcmFuZ28gaW50ZXJjdWFydMOtbGljbyBwb3IgZW5jaW1hIHkgZGViYWpvIGRlbCAxIHkgMyBjdWFydGlsLCBzw60gaGF5IHZhbG9yZXMgYXTDrXBpY29zLgoKYGBge3J9CnR0bF9tX2RscnMgPC0gZGF0YSAlPiUgc2VsZWN0KCd2YWx1ZScpCnR0bF9tX2RscnMgPC0gYXMubnVtZXJpYyh1bmxpc3QodHRsX21fZGxyc1ssMV0pKQpzdW1tYXJ5KHR0bF9tX2RscnMpWzJdIC0gMS41KklRUih0dGxfbV9kbHJzKSA+PSBzdW1tYXJ5KHR0bF9tX2RscnMpWzFdCnN1bW1hcnkodHRsX21fZGxycylbNV0gKyAxLjUqSVFSKHR0bF9tX2RscnMpIDw9IHN1bW1hcnkodHRsX21fZGxycylbNl0KYGBgCgpgYGB7cn0Kc3VtbWFyeSh0dGxfbV9kbHJzKVsyXSAtIDMqSVFSKHR0bF9tX2RscnMpID49IHN1bW1hcnkodHRsX21fZGxycylbMV0Kc3VtbWFyeSh0dGxfbV9kbHJzKVs1XSArIDMqSVFSKHR0bF9tX2RscnMpIDw9IHN1bW1hcnkodHRsX21fZGxycylbNl0KYGBgCgoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCnAgPC0gZGF0YSAlPiUgYXNfdGliYmxlICU+JSBncm91cF9ieSh5ZWFycz15ZWFyKGRhdGUpKSAlPiUKICAgIHN1bW1hcmlzZShyZW1lc2FzPXN1bSh2YWx1ZSkpICU+JQogICAgYXJyYW5nZShkZXNjKHllYXJzKSklPiUKICAgIG11dGF0ZShjaGFuZ2UgPSAocmVtZXNhcy9sZWFkKHJlbWVzYXMpIC0gMSkgKiAxMDApICU+JSAKICAgIGZpbHRlcih5ZWFycyA+IDE5OTUpICU+JSAKICAgIGZpbHRlcih5ZWFycyA8IDIwMjMpCgptZWFuX2dyb3d0aCA8LSBkYXRhICU+JSBhc190aWJibGUgJT4lIGdyb3VwX2J5KHllYXJzPXllYXIoZGF0ZSkpICU+JQogICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShyZW1lc2FzPXN1bSh2YWx1ZSkpICU+JQogICAgICAgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyh5ZWFycykpJT4lCiAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGNoYW5nZSA9IChyZW1lc2FzL2xlYWQocmVtZXNhcykgLSAxKSAqIDEwMCkgJT4lIAogICAgICAgICAgICAgICAgICAgIGZpbHRlcih5ZWFycyA+IDE5OTUpICU+JSAKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeWVhcnMgPCAyMDIyKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWVhbihjaGFuZ2UpKQoKbWVhbl9ncm93dGggPC0gbWVhbl9ncm93dGgkYG1lYW4oY2hhbmdlKWAKCmdncGxvdChwLCBhZXMoeD15ZWFycywgeT1jaGFuZ2UpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9bWVhbl9ncm93dGgpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdD0wKSArCiAgICBnZ3RpdGxlKCdDYW1iaW8gcG9yY2VudHVhbCBwb3IgYcOxbycpICsgeWxhYignJScpICsgeGxhYignTWVzJykKYGBgCkVsIGNyZWNpbWllbnRvIHByb21lZGlvIGVzIGRlIDExLjQ4JS4KCiMgNCkgUHJvbsOzc3RpY29zIGJhc2UKCiMjIERlZmluZSBsb3MgcGVyaW9kb3MgZGUgcHJ1ZWJhIHkgZW50cmVuYW1pZW50bwoKRGVwZW5kaWVuZG8gZGUgbGEgY2FudGlkYWQgZGUgZGF0b3MgeSBsYSBwZXJpb2RpY2lkYWQgcG9kcsOtYXMgZGVmaW5pciBkaWZlcmVudGUgY2FudGlkYWQgZGUgb2JzZXJ2YWNpb25lcy4gTGEgcmVnbGEgZGUgcHVsZ2FyIGVzIHVzYXIgODAlIGRlIGxvcyBkYXRvcyBwYXJhIGVudHJlbmFyIHkgZWwgcmVzdGFudGUgMjAlIHBhcmEgdmFsaWRhciBsb3MgcmVzdWx0YWRvcy4KClBhcmEgZXN0YSBzZXJpZSBkZSB0aWVtcG8gNiBtZXNlcyBkZSB2YWxpZGFjacOzbiBzb24gc3VmaWNpZW50ZXMuCgpUYW1iacOpbiBlbiBlbCBsaWJybyBbRm9yZWNhc3Q6IFByaW5jaXBsZXMgYW5kIFByYWN0aWNlXShodHRwczovL290ZXh0cy5jb20vZnBwMy90b29sYm94Lmh0bWwpIGNhcMOtdHVsbyA1CgoKYGBge3J9CnRyYWluIDwtIGRhdGEgJT4lIHNlbGVjdCh2YWx1ZSkgJT4lIGZpbHRlcl9pbmRleCgiMTk5NSBKYW4iIH4gIjIwMjMgSnVuIikKdGVzdCA8LSBkYXRhICU+JSBzZWxlY3QodmFsdWUpICU+JSBmaWx0ZXJfaW5kZXgoIjIwMjMgSnVsIiB+ICIyMDIzIERlYyIpCnRzdG5nX3ByZHMgPC0gNgpmcmNzdF9wcmRzIDwtIDYKYGBgCgojIyBTZWFzb25hbCBOYWl2ZQoKRW4gZWwgbGlicm8gW0ZvcmVjYXN0OiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvanVkZ21lbnRhbC5odG1sKSBjYXDDrXR1bG8gNSwgeSBub3RlYm9vayBkZSBjbGFzZSBbcHJvbsOzc3RpY29zIGJhc2VdKGh0dHBzOi8vaXRlc28uaW5zdHJ1Y3R1cmUuY29tL2NvdXJzZXMvMzY2NzMvZmlsZXMvNzEzMDQ3MT9tb2R1bGVfaXRlbV9pZD0xNTMzNzkyJmZkX2Nvb2tpZV9zZXQ9MSkgc2UgbXVlc3RyYW4gZGlmZXJlbnRlcyBtw6l0b2RvcyBpbmljaWFsZXMgcGFyYSBoYWNlciBlc3RpbWFjaW9uZXMuIEVzdG9zIG3DqXRvZG9zIGluaWNpYWxlcyBzb24gbGlnYWRvcyBhIGxhIGV4cGVyaWVuY2lhIGVuIGxhIHNlcmllIHkgZWwgYW7DoWxpc2lzIGV4cGxvcmF0b3JpbyByZWFsaXphZG8gY29uIGFudGVyaW9yaWRhZC4KClV0aWxpemFuZG8gbGEgZnVuY2nDs25gIGZhYmxlOjpTTkFJVkVgIHBhcmEgZWwgcHJpbWVyIHByb27Ds3N0aWNvIGJhc2UgZGViaWRvIGEgcXVlIGhheSB1bmEgY2xhcmEgZXN0YWNpb25hbGlkYWQgbWVuc3VhbC4KCmBgYHtyfQpsaWJyYXJ5KGZhYmxlKQptb2RlbHNfZml0IDwtIHRyYWluICU+JSAKICAgIG1vZGVsKGBTZWFzb25hbCBuYWl2ZWAgPSBTTkFJVkUodmFsdWUpKQptb2RlbHNfdHN0IDwtIG1vZGVsc19maXQgJT4lIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKQpzbmFpdmVfcGxvdCA8LSBtb2RlbHNfdHN0ICU+JSBhdXRvcGxvdChmaWx0ZXJfaW5kZXgoZGF0YSwgIjIwMTggSmFuIiB+IC4pKSArCiAgICBnZ3RpdGxlKCdTZWFzb25hbCBOYWl2ZScpICsgeWxhYignUmVtZXNhcycpICsgeGxhYignTWVzJykKCnNuYWl2ZV9wbG90CmBgYAojIyMgSW50ZXJ2YWxvcyBkZSBwcmVkaWNjacOzbgoKCmBgYHtyfQptb2RlbHNfdHN0Cm1vZGVsc190c3QgJT4lIGhpbG8obGV2ZWwgPSBjKDgwLCA5NSkpCmBgYAoKIyMjIEVycm9yZXMgZGUgcHJvbsOzc3RpY28KCmBgYHtyfQphY2N1cmFjeShtb2RlbHNfZml0KQpgYGAKCmBgYHtyfQoobW9kZWxzX2ZpdCAlPiUgZm9yZWNhc3QoaCA9IHRzdG5nX3ByZHMpICU+JSBhY2N1cmFjeSh0ZXN0KSkKYGBgCgojIyMgRGlhZ25vc3RpY28gZGUgcmVzaXVhbGVzCgpgYGB7cn0KYXVnID0gYXVnbWVudChtb2RlbHNfZml0KQphdWcKYGBgCgpgYGB7cn0KYXVnICU+JSBwdWxsKC5yZXNpZCkgJT4lIG1lYW4obmEucm0gPSBUUlVFKQpgYGAKCgpgYGB7cn0KYXVnICU+JSBhdXRvcGxvdCgucmVzaWQpICsgeGxhYigiTWVzIikgKyB5bGFiKCIiKSArCiAgZ2d0aXRsZSgiUmVzaWR1YWxlcyBkZWwgbcOpdG9kbyBzZWFzb25hbCBuYcOvdmUiKQpgYGAKCmBgYHtyfQphdWcgJT4lCiAgZ2dwbG90KGFlcyh4ID0gLnJlc2lkKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGdndGl0bGUoIkhpc3RvZ3JhbWEgZGUgbG9zIHJlc2lkdWFsZXMiKQpgYGAKCmBgYHtyfQphdWcgJT4lIEFDRigucmVzaWQpCmBgYAoKYGBge3J9CmF1ZyAlPiUgQUNGKC5yZXNpZCkgJT4lIGF1dG9wbG90KCkgKyBnZ3RpdGxlKCJBQ0Ygb2YgcmVzaWR1YWxzIikKYGBgCgpgYGB7cn0KdHJhaW4gJT4lIAogIG1vZGVsKFNOQUlWRSh2YWx1ZSkpICU+JSAKICBnZ190c3Jlc2lkdWFscygpCmBgYApPYnNlcnZhbW9zIHF1ZSBlc3RhcyBncsOhZmljYXMgdGllbmVuIHVuIGNvbXBvcnRhbWllbnRvIG11eSBkaXN0aW50byBhbCBOYcOvdmU6CgotIEVuIGxhIGdyw6FmaWNhIGRlIGxvcyByZXNpZHVhbGVzIHZlbW9zIHF1ZSBzZSBkaXN0aW5ndWUgY2xhcmFtZW50ZSB1biBwYXRyw7NuLiBEZSBoZWNobywgZXMgZWwgbWlzbW8gcGF0csOzbiBleGFjdGFtZW50ZSBxdWUgc2lndWVuIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCByZXN0w6FuZG9sZXMgc3UgbWVkaWEuCgotIExhIGZ1bmNpw7NuIGRlIGF1dG9jb3JyZWxhY2nDs24gdGllbmUgdW4gY29tcG9ydGFtaWVudG8gdMOtcGljbyBkZSB1bmEgY2FtaW5hdGEgYWxlYXRvcmlhLiBQb3IgbG8gdGFudG8sIGxhcyBhdXRvY29ycmVsYWNpb25lcyBzb24gc2lnbmlmaWNhdGl2YXMuCgotIEVsIGhpc3RvZ3JhbWEgZGUgbG9zIHJlc2lkdW9zIG11ZXN0cmEgY2xhcmFtZW50ZSBxdWUgbm8gc2UgZGlzdHJpYnV5ZW4gZGUgbWFuZXJhIG5vcm1hbC4KCiMjIyBUZXN0IGRlIExqdW5nLUJveApVbiB0ZXN0IHJlbGFjaW9uYWRvIHkgcXVlLCBnZW5lcmFsbWVudGUsIGVzIG3DoXMgcHJlY2lzbyBlcyBlbCB0ZXN0IGRlIExqdW5nLUJveC4KCkVuIGVzdGUgY2FzbyBlcyBpZ3VhbDogdmFsb3JlcyBncmFuZGVzIGRlIGxhIHBydWViYSBzb24gaW5kaWNpb3MgZGUgcXVlIGxhcyBhdXRvY29ycmVsYWNpb25lcyBubyBwcm92aWVuZW4gZGUgcnVpZG8gYmxhbmNvLgoKPkVudG9uY2VzLCBsYSBoaXDDs3Rlc2lzIG51bGEgZGUgZXN0YXMgcHJ1ZWJhcyBlcyBxdWUgbGEgc2VyaWUgZW4gY3Vlc3Rpw7NuIG5vIGVzdMOhIGF1dG9jb3JyZWxhY2lvbmFkYS4gRW4gb3RyYXMgcGFsYWJyYXMsIGxhIEgwIGRpY2UgcXVlIGxhIHNlcmllIGVzIHJ1aWRvIGJsYW5jby4gU2kgzrEgZXMgZWwgbml2ZWwgZGUgc2lnbmlmaWNhbmNpYSAoZWwgbml2ZWwgbcOheGltbyBkZSBlcnJvciBxdWUgZXN0YW1vcyBkaXNwdWVzdG9zIGEgYWNlcHRhcikgeSBzaSBlbCDCqHAtdmFsdWUgPM6xLCBlbnRvbmNlcyByZWNoYXphbW9zIEgwLCBkZSBsbyBjb250cmFyaW8sIG5vIHJlY2hhemFtb3MgbGEgSDAuCgpgYGB7cn0KYXVnICU+JSBmZWF0dXJlcygucmVzaWQsIGxqdW5nX2JveCwgbGFnPTEyLCBkb2Y9MCkKYGBgCgoKIyA1KSBEZXNjb21wb3NpY2nDs24KCiMjIENvbXBvbmVudGVzIHkgZGVzY29tcG9zaWNpw7NuIFNUTAoKQ29uc3VsdGEgZWwgbGlicm8gW0ZvcmVjYXN0OiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZV0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvdHJhbnNmb3JtYXRpb25zLmh0bWwpIGNhcMOtdHVsbyAzLjYKClBhcmEgZXN0dWRpYXIgbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgdGVuZGVuY2lhIHkgZXN0YWNpb25hbGlkYWQgc2UgZGVzY29tcG9uZSB5IGdyYWZpY2FyIHVzYW5kbyBlbCBtw6l0b2RvIFNUTC4KTGEgZnVuY2nDs24gcGVyIGRlICpwZXJpb2RpY2l0eSogZXMgbGEgbcOhcyBhZGVjdWFkYSBhbCBwYXLDoW1ldHJvIGRlIHN1YXZpemFjacOzbiBlc3RhY2lvbmFsICpzLndpbmRvd3MqLiBFcyBlbCBjb21wb25lbnRlIGVzdGFjaW9uYWwgZXMgZWwgbcOhcyBwZXF1ZcOxbywgc2llbmRvIGxhIHRlbmRlbmNpYSBtw6FzIHNpZ25pZmljYXRpdmEuCgpQb2RlbW9zIG9ic2VydmFyIGVuIGVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCBjYXB0dXJhLCBxdWUgZWwgaW5pY2lvIGRlIGHDsW8gZXMgZWwgbcOhcyBmbG9qaXRvIHkgcXVlLCBhIG1lZGlhZG9zIGRlbCBhw7FvIGVzIGN1YW5kbyBzZSBlbnbDrWFuIG3DoXMgcmVtZXNhcy4KCkVsIGNvbXBldGVudGUgZGUgdGVuZGVuY2lhIHkgcmVtYW5lbnRlcyBvYnNlcnZhbW9zIHkgY29uZmlybWFtb3MgbG8gbWVuY2lvbmFkbyBhbnRlcmlvcm1lbnRlLiBBIHBhcnRpciBkZWwgMjAwNCBlbCBlbnbDrW8gZGUgcmVtZXNhcywgbyBlbCByZWdpc3RybywgYXVtZW50YSBjb25zaWRlcmFibGVtZW50ZSB5IGVsIGVuIDIwMDggeSAyMDIwIGVsIHJlbWFuZW50ZSBlcyBtdXkgc2lnbmlmaWNhdGl2by4gVGFtYmnDqW4sIGEgcGFydGlyIGRlbCAyMDIwIGVsIGNvbXBvbmVudGUgcmVtYW5lbnRlIHNlIGFjZW50w7phLgoKIVtdKC4vaW1hZ2VzL3N0bC5wbmcpCgo+IENsZXZlbGFuZCwgUi4gQi4sIENsZXZlbGFuZCwgVy4gUy4sIE1jUmFlLCBKLiBFLiwgJiBUZXJwZW5uaW5nLCBJLiBKLiAoMTk5MCkuIFNUTDogQSBzZWFzb25hbC10cmVuZCBkZWNvbXBvc2l0aW9uIHByb2NlZHVyZSBiYXNlZCBvbiBsb2Vzcy4gSm91cm5hbCBvZiBPZmZpY2lhbCBTdGF0aXN0aWNzLCA2KDEpLCAz4oCTMzMuIFtodHRwOi8vYml0Lmx5L3N0bDE5OTBdKGh0dHA6Ly9iaXQubHkvc3RsMTk5MCkKCmBgYHtyfQpzdGxfbW9kZWwgPSBkYXRhICU+JSBkcGx5cjo6c2VsZWN0KHZhbHVlKSAlPiUgc3RsKHMud2luZG93ID0gJ3BlcicpCnBsb3Qoc3RsX21vZGVsLG1haW4gPSAnRGVzY29tcG9zaWPDs24gZGUgbGEgc2VyaWUgY29uIFNUTCcpCmBgYAoKCgoKIyMgVHJhbnNmb3JtYWNpb25lcyB5IGFkanVzdGVzCgpDb25zdWx0YSBlbCBsaWJybyBbRm9yZWNhc3Q6IFByaW5jaXBsZXMgYW5kIFByYWN0aWNlXShodHRwczovL290ZXh0cy5jb20vZnBwMy90cmFuc2Zvcm1hdGlvbnMuaHRtbCkgY2Fww610dWxvIDMuMQoKQWp1c3RhciBsb3MgZGF0b3MgaGlzdMOzcmljb3MgYSBtZW51ZG8gcHVlZGUgY29uZHVjaXIgYSBzZXJpZXMgdGVtcG9yYWxlcyBtw6FzIHNpbXBsZXMuIGVqZW1wbG9zIGRlIHB1ZWRlbiBzZXIgYWp1c3RlcyBkZSBjYWxlbmRhcmlvLCBhanVzdGVzIGRlIHBvYmxhY2nDs24sIGFqdXN0ZXMgZGUgaW5mbGFjacOzbiB5IHRyYW5zZm9ybWFjaW9uZXMgbWF0ZW3DoXRpY2FzLiBFbCBwcm9ww7NzaXRvIGRlIGVzdG9zIGFqdXN0ZXMgeSB0cmFuc2Zvcm1hY2lvbmVzIGVzIHNpbXBsaWZpY2FyIGxvcyBwYXRyb25lcyBlbiBsb3MgZGF0b3MgaGlzdMOzcmljb3MgZWxpbWluYW5kbyBmdWVudGVzIGNvbm9jaWRhcyBkZSB2YXJpYWNpw7NuIG8gaGFjaWVuZG8gcXVlIGVsIHBhdHLDs24gc2VhIG3DoXMgY29uc2lzdGVudGUgZW4gdG9kbyBlbCBjb25qdW50byBkZSBkYXRvcy4gTG9zIHBhdHJvbmVzIG3DoXMgc2ltcGxlcyBzdWVsZW4gc2VyIG3DoXMgZsOhY2lsZXMgZGUgbW9kZWxhciB5IGNvbmR1Y2VuIGEgcHJvbsOzc3RpY29zIG3DoXMgcHJlY2lzb3MuCgpDw7NtbyB5YSBzZSBtZW5jaW9uw7MgYW50ZXJpb3JtZW50ZSBjb24gZWwgaGlzdG9ncmFtYSwgeSBjb24gb2JzZXJ2YXIgbGFzIG1hZ25pdHVkZXMgZGUgbG9zIHZhbG9yZXMgYWwgaW5pY2lvIHkgZmluYWwgZGUgbGEgc2VyaWUgZXMgY29udmVuaWVudGUgdW5hIHRyYW5zZm9ybWFjacOzbiBkZSBwb3RlbmNpYS4gRWwgaGlzdG9ncmFtYSB5IGVsIHNlc2dvIHRhbWJpw6luIG11ZXN0cmEgc2VzZ28gbmVnYXRpdm8uIEFkZW3DoXMsIGxhIHBydWViYSBkZSBub3JtYWxpZGFkIFNIQVBJUk8tV0lMSyAoYWxwaGEgPSAwLjA1KQoKCmBgYHtyfQpxcW5vcm0oZGF0YSR2YWx1ZSkKcXFsaW5lKGRhdGEkdmFsdWUpCmBgYAoKVmFsb3IgcCBlcyBtZW5vciBxdWUgQWxwaGEgZW50b25jZXMgbm8gdGVuZW1vcyB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuIERlIGhlY2hvLCBlc3RhbW9zIGxlam9zIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCBwZXJvIGVsIHNlc2dvIG5vIGVzdGEgdGFuIG1hbCBzaWVuZG8gMC41LiBRdWVyZW1vcyBsbyBtw6FzIGFjZXJjYWRvIGEgMCBkZSBzZXNnby4KCkF1bnF1ZSBzZSBwdWVkZSBlbnRlbmRlciB5IHVzYXIgbGEgdHJhbnNmb3JtYWNpw7NuIGJveCBjb3ggcGFyYSB0cmFuc2Zvcm1hciB1bmEgZGlzdHJpYnVjacOzbiBubyBub3JtYWwgYSB1bmEgbm9ybWFsLCBlbCBwcmluY2lwYWwgb2JqZXRpdm8gZXMgY29ycmVnaXIgc2VzZ29zIHkgdmFyaWFuemFzIGRlc2lndWFsZXMgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCiQkCnkoXGxhbWJkYSkgPVxiZWdpbntjYXNlc30KICAgIFxmcmFjeyh5XlxsYW1iZGEgLSAxKX17XGxhbWJkYX0mIFxsYW1iZGEgXG5lcSAwICxcXAogICAgbG9nKHkpJiAgIFxsYW1iZGEgPSAwLgogICAgXGVuZHtjYXNlc30KJCQKCkVsIGxhbWJkYSDDs3B0aW1vIHNlIGVuY3VlbnRyYSBjdWFuZG8gc2UgZW5jdWVudHJhIGxhIGRpc3RyaWJ1Y2nDs24gY29uIGVsIG3DoXhpbW8gbG9nbGlrZWxpaG9vZCBvIGNvbiBlbCBjb2VmaWNpZW50ZSBkZWwgZ3LDoWZpY28gZGUgcHJvYmFiaWxpZGFkIGRlIGNvcnJlbGFjacOzbiBvIFNoYXBpcm8tV2lsay4KCj4gQmlja2VsLCBQLiBKLiwgJiBEb2tzdW0sIEsuIEEuICgxOTgxKS4gQW4gYW5hbHlzaXMgb2YgdHJhbnNmb3JtYXRpb25zIHJldmlzaXRlZC4gSm91cm5hbCBvZiB0aGUgQW1lcmljYW4gU3RhdGlzdGljYWwgQXNzb2NpYXRpb24sIDc2KDM3NCksIDI5NuKAkzMxMS4gW0RPSV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwODAvMDE2MjE0NTkuMTk4MS4xMDQ3NzY0OSkKPiAKPiBCb3gsIEcuIEUuIFAuLCAmIENveCwgRC4gUi4gKDE5NjQpLiBBbiBhbmFseXNpcyBvZiB0cmFuc2Zvcm1hdGlvbnMuIEpvdXJuYWwgb2YgdGhlIFJveWFsIFN0YXRpc3RpY2FsIFNvY2lldHkuIFNlcmllcyBCLCBTdGF0aXN0aWNhbCBNZXRob2RvbG9neSwgMjYoMiksIDIxMeKAkzI1Mi4gW0RPSV0oaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvai4yNTE3LTYxNjEuMTk2NC50YjAwNTUzLngpCgoKYGBge3J9CmJjIDwtIEVudlN0YXRzOjpib3hjb3goZGF0YSR2YWx1ZSwgbGFtYmRhPWMoLTIsIDIpLCBvcHRpbWl6ZT1UUlVFLCBvYmplY3RpdmUubmFtZT0nTG9nLUxpa2VsaWhvb2QnKQpiY19kYXRhIDwtIEVudlN0YXRzOjpib3hjb3hUcmFuc2Zvcm0oZGF0YSR2YWx1ZSwgYmMkbGFtYmRhKQoKc2tld25lc3MoYmNfZGF0YSkKc2hhcGlyby50ZXN0KGJjX2RhdGEpCnFxbm9ybShiY19kYXRhKQpxcWxpbmUoYmNfZGF0YSkKCmRhdGEgPC0gZGF0YSAlPiUgbXV0YXRlKCdyZW1lc2FzX3RybicgPSBiY19kYXRhKQpgYGAKCgojIDYpIFByb27Ds3N0aWNvcyBiYXNlIGNvbiBTVEwgeSB0cmFuZm9ybWFjacOzbiBtYXRlbcOhdGljYQoKIyMgU1RMIFNlYXNvbmFsIE5haXZlCgpgYGB7cn0KbW9kZWxzX2ZpdCA8LSB0cmFpbiAlPiUgCiAgbW9kZWwoc3RsZiA9IGRlY29tcG9zaXRpb25fbW9kZWwoCiAgICBTVEwodmFsdWUgfiB0cmVuZCh3aW5kb3cgPSAxMiksIHJvYnVzdCA9IFRSVUUpLAogICAgTkFJVkUoc2Vhc29uX2FkanVzdCkKICApKQptb2RlbHNfdHN0IDwtIG1vZGVsc19maXQgJT4lIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKQptYXBlX3NuIDwtIChtb2RlbHNfZml0ICU+JSBmb3JlY2FzdChoID0gdHN0bmdfcHJkcykgJT4lIGFjY3VyYWN5KHRlc3QpKSRNQVBFCnNuYWl2ZV9wbG90IDwtIG1vZGVsc190c3QgJT4lIGF1dG9wbG90KGZpbHRlcl9pbmRleChkYXRhLCAiMjAxOCBKYW4iIH4gLikpICsKICAgIGdndGl0bGUoJ1NUTCcpICsgeWxhYignUmVtZXNhcycpICsgeGxhYignTWVzJykKCnNuYWl2ZV9wbG90CmBgYApgYGB7cn0KP2RlY29tcG9zaXRpb25fbW9kZWwKYGBgCgpgYGB7cn0KbW9kZWxzX2ZpdCA8LSB0cmFpbiAlPiUgCiAgbW9kZWwoc3RsZiA9IGRlY29tcG9zaXRpb25fbW9kZWwoCiAgICBTVEwobG9nKHZhbHVlKSB+IHRyZW5kKHdpbmRvdyA9IDEyKSwgcm9idXN0ID0gVFJVRSksCiAgICBOQUlWRShzZWFzb25fYWRqdXN0KQogICkpCm1vZGVsc190c3QgPC0gbW9kZWxzX2ZpdCAlPiUgZm9yZWNhc3QoaCA9IHRzdG5nX3ByZHMpCm1hcGVfc24gPC0gKG1vZGVsc19maXQgJT4lIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKSAlPiUgYWNjdXJhY3kodGVzdCkpJE1BUEUKc25haXZlX3Bsb3QgPC0gbW9kZWxzX3RzdCAlPiUgYXV0b3Bsb3QoZmlsdGVyX2luZGV4KGRhdGEsICIyMDE4IEphbiIgfiAuKSkgKwogICAgZ2d0aXRsZSgnU2Vhc29uYWwgTmFpdmUnKSArIHlsYWIoJ1JlbWVzYXMnKSArIHhsYWIoJ01lcycpCgpzbmFpdmVfcGxvdApgYGAKCgpgYGB7cn0KbW9kZWxzX2ZpdCA8LSB0cmFpbiAlPiUgCiAgbW9kZWwoCiAgICBgU2Vhc29uYWwgbmFpdmVgID0gU05BSVZFKHZhbHVlKSwKICAgIHN0bGYgPSBkZWNvbXBvc2l0aW9uX21vZGVsKAogICAgU1RMKHZhbHVlIH4gdHJlbmQod2luZG93ID0gMTIpLCByb2J1c3QgPSBUUlVFKSwKICAgIE5BSVZFKHNlYXNvbl9hZGp1c3QpKSwKICAgIGxvZ19zdGxmID0gZGVjb21wb3NpdGlvbl9tb2RlbCgKICAgICAgICAgICAgU1RMKGxvZyh2YWx1ZSkgfiB0cmVuZCh3aW5kb3cgPSAxMiksIHJvYnVzdCA9IFRSVUUpLAogICAgICAgICAgICBOQUlWRShzZWFzb25fYWRqdXN0KSkKICApCm1vZGVsc190c3QgPC0gbW9kZWxzX2ZpdCAlPiUgZm9yZWNhc3QoaCA9IHRzdG5nX3ByZHMpCm1hcGVfc24gPC0gKG1vZGVsc19maXQgJT4lIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKSAlPiUgYWNjdXJhY3kodGVzdCkpJE1BUEUKc25haXZlX3Bsb3QgPC0gbW9kZWxzX3RzdCAlPiUgYXV0b3Bsb3QoZmlsdGVyX2luZGV4KGRhdGEsICIyMDE4IEphbiIgfiAuKSwgbGV2ZWwgPSBOVUxMKSArCiAgICBnZ3RpdGxlKCdEaWZlcmVudGVzIG1vZGVsb3MnKSArIHlsYWIoJ1JlbWVzYXMnKSArIHhsYWIoJ01lcycpCgpzbmFpdmVfcGxvdApgYGAKCmBgYHtyfQptb2RlbHNfdHN0Cm1vZGVsc190c3QgJT4lIGhpbG8obGV2ZWwgPSBjKDgwLCA5NSkpCmBgYAoKYGBge3J9CmFjY3VyYWN5KG1vZGVsc19maXQpCmBgYAoKYGBge3J9CnRyYWluICU+JSAKICBtb2RlbChkZWNvbXBvc2l0aW9uX21vZGVsKAogICAgICAgICAgICBTVEwobG9nKHZhbHVlKSB+IHRyZW5kKHdpbmRvdyA9IDEyKSwgcm9idXN0ID0gVFJVRSksCiAgICAgICAgICAgIE5BSVZFKHNlYXNvbl9hZGp1c3QpKSkgJT4lIAogIGdnX3RzcmVzaWR1YWxzKCkKYGBgCgoKYGBge3J9Cm1vZGVsc19maXRbM10gJT4lIGdnX3RzcmVzaWR1YWxzKCkKYGBgCgojIDcpIFJlZ3Jlc2nDs24gTGluZWFsCgojIyBEYXRvcwoKTGEgaGlww7N0ZXNpcyByYWRpY2EgZW4gbGEgcmVsYWNpw7NuIHkgY3JlY2ltaWVudG8gZWNvbsOzbWljbyBkZSBFc3RhZG9zIFVuaWRvcyBwcmVjZWRlIHkgZXN0YSBjb3JyZWxhY2lvbmFkbyBhIGVsIGluY3JlbWVudG8gZGUgcmVtZXNhcy4gUG9yIGxvIHF1ZSBlbCDDrW5kaWNlICoqQnJhdmUtQnV0dGVycy1LZWxsZXkqKiBwdWVkZSBzZXIgcmVsZXZhbnRlIHBhcmEgbGEgZXN0aW1hY2nDs24gZGUgbGFzIHJlbWVzYXMuCgo+VGhlIEJyYXZlLUJ1dHRlcnMtS2VsbGV5IEluZGV4ZXMgKEJCS0kpIGFyZSBhIHJlc2VhcmNoIHByb2plY3Qgb2YgdGhlIEZlZGVyYWwgUmVzZXJ2ZSBCYW5rIG9mIENoaWNhZ28uIFRoZSBCQksgQ29pbmNpZGVudCBhbmQgTGVhZGluZyBJbmRleGVzIGFuZCBNb250aGx5IEdEUCBHcm93dGggZm9yIHRoZSBVLlMuIGFyZSBjb25zdHJ1Y3RlZCBmcm9tIGEgY29sbGFwc2VkIGR5bmFtaWMgZmFjdG9yIGFuYWx5c2lzIG9mIGEgcGFuZWwgb2YgNTAwIG1vbnRobHkgbWVhc3VyZXMgb2YgcmVhbCBlY29ub21pYyBhY3Rpdml0eSBhbmQgcXVhcnRlcmx5IHJlYWwgR0RQIGdyb3d0aC4KCkxvcyBkYXRvcyBtZW5zdWFsZXMgc2UgcHVlZGVuIG9idGVuZXIgZGUgbGEgRlJFRC4gW2h0dHBzOi8vZnJlZC5zdGxvdWlzZmVkLm9yZy9zZXJpZXMvQkJLTUdEUF0oaHR0cHM6Ly9mcmVkLnN0bG91aXNmZWQub3JnL3Nlcmllcy9CQktNR0RQKQoKSW1wb3J0YXIgbG9zIGRhdG9zIHVzYW5kbyB0aWR5cXVhbnQKYGBge3J9CmxpYnJhcnkodGlkeXF1YW50KQpnZHBfdXMgPSB0cV9nZXQoIkJCS01HRFAiLCBnZXQ9ImVjb25vbWljLmRhdGEiLCBmcm9tID0gIjE5OTUtMDEtMDEiKQpgYGAKCiMjIyBDb252ZXJ0aXIgbGEgZmVjaGEgeSBkYXRhZnJhbWUgYSB0c2liYmxlCmBgYHtyfQpnZHBfdXMkZGF0ZSA9IHllYXJtb250aChnZHBfdXMkZGF0ZSkKZ2RwX3VzID0gZ2RwX3VzICU+JSBzZWxlY3QoZGF0ZSwgcHJpY2UpICU+JSBhc190c2liYmxlKGluZGV4PWRhdGUsIHJlZ3VsYXIgPSBUUlVFKQpnZHBfdXMKYGBgCiMjIyBDb252ZXJ0aXIgZWwgY2FtYmlvIHBlcmNlbnR1YWwgYSBiYXNlIDEwMCBjb24gbGEgc3VtYSBhY3VtdWxhZGEgZGUgbG9zIGNhbWJpb3MgcG9yY2VudHVhbGVzCgpgYGB7cn0KZ2RwX3VzID0gZ2RwX3VzICU+JSBtdXRhdGUoZ2RwX2Jhc2UgPSBjdW1zdW0ocHJpY2UpKQpgYGAKCgpgYGB7cn0KZ2RwX3VzCmBgYAoKIyMjIFNlcGFyYSBlbnRyZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhCgpgYGB7cn0KdHJhaW5fZ2RwIDwtIGdkcF91cyAlPiUgc2VsZWN0KGdkcF9iYXNlKSAlPiUgZmlsdGVyX2luZGV4KCIxOTk1IEphbiIgfiAiMjAyMyBKdW4iKQp0ZXN0X2dkcCA8LSBnZHBfdXMgJT4lIHNlbGVjdChnZHBfYmFzZSkgJT4lIGZpbHRlcl9pbmRleCgiMjAyMyBKdWwiIH4gIjIwMjMgRGVjIikKYGBgCgojIyMgUmVub21icmFyIGxhcyBjb2x1bW5hcwpgYGB7cn0KdHJhaW5fZ2RwID0gYWRkX2NvbHVtbih0cmFpbl9nZHAsIHRyYWluJHZhbHVlKSAjYXBwZW5kIHJlbWVzYXMKY29sbmFtZXModHJhaW5fZ2RwKVszXSA9ICJyZW1lc2FzIiAKY29sbmFtZXModHJhaW5fZ2RwKVsxXSA9ICJnZHAiCnRlc3RfZ2RwID0gYWRkX2NvbHVtbih0ZXN0X2dkcCwgdGVzdCR2YWx1ZSkKY29sbmFtZXModGVzdF9nZHApWzNdID0gInJlbWVzYXMiCmNvbG5hbWVzKHRlc3RfZ2RwKVsxXSA9ICJnZHAiCmBgYAoKIyMjIEdyw6FmaWNhIGRlIGFtYmFzIHNlcmllcyBhIHRyYXZlcyBkZWwgdGllbXBvCgpgYGB7cn0KdHJhaW5fZ2RwIHw+CiAgcGl2b3RfbG9uZ2VyKGMocmVtZXNhcywgZ2RwKSwgbmFtZXNfdG89IlNlcmllcyIpIHw+CiAgYXV0b3Bsb3QodmFsdWUpICsKICBsYWJzKHkgPSAidmFsdWUiKQpgYGAKCiMjIyBHcsOhZmljYSBkZSBkaXNwZXJjacOzbiAoY29ycmVsYWNpw7NuKSBlbnRyZSBhbWJhcyBzZXJpZXMKCmBgYHtyfQp0cmFpbl9nZHAgJT4lIGdncGxvdChhZXMoeCA9IGdkcCwgeSA9IGxvZyhyZW1lc2FzKSkpICsKICBsYWJzKHkgPSAiUmVtZXNhcyIsCiAgICAgICB4ID0gIkdEUCBCYXNlIDEiKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQpgYGAKCgoKIyMgQWp1c3RlIGRlIHJlZ3Jlc2nDs24geSByZXBvcnRlIGRlbCBtb2RlbG8KCmBgYHtyfQpmaXRfbG0gPC0gdHJhaW5fZ2RwIHw+CiAgbW9kZWwodHNsbSA9IFRTTE0obG9nKHJlbWVzYXMpIH4gZ2RwKSkKCnJlcG9ydChmaXRfbG0pCmBgYAoKCgojIyMgVGFibGEgYXVtZW50YWRhIGRlbCBhanVzdGUgZGVsIG1vZGVsbwoKYGBge3J9CmF1Z21lbnQoZml0X2xtKQpgYGAKCiMjIyBWYWxvcmVzIGNhbGN1bGFkb3MKCmBgYHtyfQpwbG90X2xtID0gYXVnbWVudChmaXRfbG0pIHw+CiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsKICBnZW9tX2xpbmUoYWVzKHkgPSByZW1lc2FzLCBjb2xvdXIgPSAicmVhbGVzIikpICsKICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvdXIgPSAiYWp1c3RhZG9zIikpICsKICBsYWJzKHkgPSBOVUxMLAogICAgdGl0bGUgPSAicmVtZXNhcyIKICApICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpCmdncGxvdGx5KHBsb3RfbG0pCmBgYAoKCiMjIyBab29tIGEgbGEgZ3LDoWZpY2EgZGVzcHVlcyBkZSAyMDE4CgpgYGB7cn0KYXVnbWVudChmaXRfbG0pIHw+IGZpbHRlcih5ZWFyKGRhdGUpID4gMjAxOCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsKICBnZW9tX2xpbmUoYWVzKHkgPSByZW1lc2FzLCBjb2xvdXIgPSAicmVhbGVzIikpICsKICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvdXIgPSAiYWp1c3RhZG9zIikpICsKICBsYWJzKHkgPSBOVUxMLAogICAgdGl0bGUgPSAicmVtZXNhcyIKICApICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpCmBgYAojIyMgQW5hbGlzaXMgZGUgcmVzaWR1YWxlcwoKUHJpbWVyYSBncsOhZmljYTogZXJyb3JlcyBlbiBlbCB0aWVtcG8gcGFyYSBldmFsdWFyIG1lZGlhIHkgdmFyaWFuemEuClNlZ3VuZGEgZ3LDoWZpY2E6IGF1dG9jb3JyZWxhY2nDs24gZGUgZXJyb3JlczogZXJyb3JlcyBlbiBlbCBwYXNhZG8gYWZlY3RhbiBlbCB2YWxvciBhY3R1YWwuCnRlcmNlcmEgZ3LDoWZpY2E6IGhpc3RvZ3JhbWEgZGUgZXJyb3JlcyBwYXJhIHZlcmlmaWNhciBsYSBub3JtYWxpZGFkLCBtZWRpYSBzZWEgMCB5IHNlc2dvIHNlYSAwLgoKYGBge3J9CmZpdF9sbSAlPiUgZ2dfdHNyZXNpZHVhbHMoKQpgYGAKCmBgYHtyfQphdWdtZW50KGZpdF9sbSkgJT4lIGZlYXR1cmVzKC5pbm5vdiwgbGp1bmdfYm94LCBsYWc9MTIpCmBgYApMb3MgcmVzaWR1YWxlcyBlc3RhbiBhdXRvY29ycmVsYWNpb25hZG9zLCBubyBzb24gaG9tb2NlZGFzdGljb3MgeSBsYSBtZWRpYSBkZSBsb3MgZXJyb3JlcyBubyBlcyBjZXJvLgoKIyMjIEdyw6FmaWNhIGRlIHJlc2lkdWFsZXMgY29udHJhIHZhbG9yZXMgYWp1c3RhZG9zLgpUYW1iacOpbiBzaXJ2ZSBwYXJhIGlkZW50aWZpY2FyIG91dGxpZXJzCgpgYGB7cn0KYXVnbWVudChmaXRfbG0pICU+JSBnZ3Bsb3QoYWVzKHg9LmZpdHRlZCwgeT0ucmVzaWQpKSArIGdlb21fcG9pbnQoKSArIGxhYnMoeD0iRml0dGVkIiwgeT0iUmVzaWR1YWxzIikKYGBgCgpMb3MgZGF0b3MgZGUgYW1iYXMgdmFyaWFibGVzIG5vIHNvbiBlc3RhY2lvbmFyaWFzLCBlcyBkZWNpciB0aWVuZW4gdGVuZGVuY8OtYS4gWSBwdWVkZSBzZXIgcXVlIG5pbmd1YSB0ZW5nYSByZWxhY2nDs24gY29uIGxhIG90cmEgbmkgc2VhIGNhdXNhbCBkZSBsYSBvdHJhLgoKUmVncmVzaW9uZXMgY29uIHJlbGFjaW9uZXMgZXNwdXJpYXMgcHVlZGUgcXVlIGZ1bmNpb25lbiBlbiBlbCBjb3J0byBwbGF6byBwZXJvIGRlamVuIGRlIGZ1bmNpb25hciBlbiBlbCBmdXR1cm8uCgojIyMgUHJvbsOzc3RpY29zCgpgYGB7cn0KdGVzdF9nZHBbYygiZ2RwIiwgImRhdGUiKV0KYGBgCgojIyMgR3LDoWZpY2EgZGUgcHLDs25vc3RpY29zCmBgYHtyfQpmY19sbSA9IGZvcmVjYXN0KGZpdF9sbSwgbmV3X2RhdGEgPSB0ZXN0X2dkcFtjKCJnZHAiLCAiZGF0ZSIpXSkKZGF0YSAlPiUgYXV0b3Bsb3QodmFsdWUpICsgYXV0b2xheWVyKGZjX2xtKQpgYGAKIyMjIFRhYmxlIGRlIHByw7Nub3N0aWNvcwoKYGBge3J9CmZjX2xtCmBgYAoKIyMgT3RyYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzCgojIyMgVGVuZGVuY2lhCgokJCAKeV97dH0gPSBcYmV0YV97MH0gKyBcYmV0YV97MX10ICsgXGVwc2lsb25fe3R9CiQkCmTDs25kZSAkIHQgPSB7MSwyLDMsIC4uLiwgbn0gJAoKYFRTTE1gIHV0aWxpemEgZWwgYXJndW1lbnRvIGB0cmVuZCgpYCBwYXJhIGluZGljYXIgbGEgdGVuZGVuY2lhLgoKIyMjIEVzdGFjaW9uYWxpZGFkCgpQYXJhIGVzdGFjaW9uYWxpZGFkIGRpYXJpYSwgY2FsY3VsYSA2IHZhcmlhYmxlczoKCiQkCnlfe3R9ID0gXGJldGFfezB9ICsgXGJldGFfezF9bHVuZXMgKyBcYmV0YV97Mn1tYXJ0ZXMgKyBcYmV0YV97M31taWVyY29sZXMgKyBcYmV0YV97NH1qdWV2ZXMgKyBcYmV0YV97NX12aWVybmVzICsgXGJldGFfezZ9c2FiYWRvICsgXGVwc2lsb25fe3R9CiQkCgohW10oLi9pbWFnZXMvZHVtbXlfZGlhcmlvLnBuZykKCgpQYXJhIGVzdGFjaW9uYWxpZGFkIG1lbnN1YWwsIGNhbGN1bGEgMTEgdmFyaWFibGVzOgoKJCQKeV97dH0gPSBcYmV0YV97MH0gKyBcYmV0YV97MX1lbmVybyArIFxiZXRhX3syfWZlYnJlcm8gKyBcYmV0YV97M31tYXJ6byArIFxiZXRhX3s0fWFicmlsICsgXGJldGFfezV9bWF5byArIFxiZXRhX3s2fWp1bmlvICsgXGJldGFfezd9anVsaW8gKyBcYmV0YV97OH1hZ29zdG8gKyBcYmV0YV97OX1zZXB0aWVtYnJlICsgXGJldGFfezEwfW9jdHVicmUgKyBcYmV0YV97MTF9bm92aWVtYnJlICsgXGVwc2lsb25fe3R9CiQkCgpQYXJhIGVzdGFjaW9uYWxpZGFkIHRyaW1lc3RyYWwgY2FsY3VsYSAzIHZhcmlhYmxlcy4KUGFyYSBlc3RhY2lvbmFsaWRhZCBkaWFyaWEgY2FsY3VsYSAyMyB2YXJpYWJsZXMuClBhcmEgZXN0YWNpb25hbGlkYWQgc2VtYW5hbCBjYWxjdWxhIDUxIHZhcmlhYmxlcy4KCmBUU0xNYCB1dGlsaXphIGVsIGFyZ3VtZW50byBgc2Vhc29uKClgIHBhcmEgaW5kaWNhciBsYSBlc3RhY2lvbmFsaWRhZC4KCiMjIyBBdGlwaWNvcy9TcGlrZS9JbnRlcnZlbnRpb24gdmFyaWFibGUKCkNvbiBsbyBvYnNlcnZhZG8gZW4gbGFzIGdyw6FmaWNhIHkgbG8gY29tZW50YWRvIGFudGVyaW9ybWVudGUsIGxhcyBmZWNoYXMgZGUgaW1wb3J0YW5jaWEgcGFyYSBlc3RhIHNlcmllIGRlIHRpZW1wbyBzb24gbGEgY3Jpc2lzIGhpcG90ZWNhcmlhIHkgbGEgY3Jpc2lzIGNvdmlkLgpEZWZpbmlyIHVuYSBjb2x1bW5hIGRlIDBzLCBleGVwdG8gZW50cmUgbGFzIGZlY2hhcyBpbmRpY2FkYXMgKGRpY2llbWJyZSAyMDA3IC0ganVuaW8gMjAwOSkuCkRlZmluaXIgdW5hIGNvbHVtbmEgZGUgMHMsIGV4ZXB0byBlbnRyZSBsYXMgZmVjaGFzIGluZGljYWRhcyAoZmVicmVybyAyMDIwIC0gbWF5byAyMDIwKS4KCkNyZWEgdW5hIHZhcmlhYmxlIGR1bW15IGRlIHZhbG9yZXMgYXTDrXBpY29zIHBhcmEgdHUgc2VyaWUgZGUgdGllbXBvCgpgYGB7cn0KdHJhaW5fZ2RwID0gdHJhaW5fZ2RwICU+JQogICAgbXV0YXRlKGNyaXNpc2ggPSBpZl9lbHNlKGRhdGUgPj0gYXMuRGF0ZSgiMjAwNy0xMi0wMSIpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlIDw9IGFzLkRhdGUoIjIwMDktMDYtMDEiKSwgMSwgMCksCiAgICAgICAgICAgY3Jpc2lzYyA9IGlmX2Vsc2UoZGF0ZSA+PSBhcy5EYXRlKCIyMDIwLTAyLTAxIikgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGUgPD0gYXMuRGF0ZSgiMjAyMC0wNS0wMSIpLCAxLCAwKQogICAgICAgICAgICkKdGVzdF9nZHAgPSB0ZXN0X2dkcCAlPiUKICAgIG11dGF0ZShjcmlzaXNoID0gaWZfZWxzZShkYXRlID49IGFzLkRhdGUoIjIwMDctMTItMDEiKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZSA8PSBhcy5EYXRlKCIyMDA5LTA2LTAxIiksIDEsIDApLAogICAgICAgICAgIGNyaXNpc2MgPSBpZl9lbHNlKGRhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMi0wMSIpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlIDw9IGFzLkRhdGUoIjIwMjAtMDUtMDEiKSwgMSwgMCkKICAgICAgICAgICApCmBgYAoKIyMjIFJlemFnb3MKClVzYSBsb3MgdmFsb3JlcyBwYXNhZG9zIGPDs21vIHByZWRpY3RvciBkZSBYLiBIaXBvdGVzaXM6IExhcyByZW1lc2FzIChvIGVsIGNhbWJpbyBwb3JjZW50dWFsKSBlbnZpYWRhcyBlbiBlbCBwYXNhZG8gaW5mbHV5ZW4gZW4gZWwgZnV0dXJvLiBSZXZpc2EgbGEgYXV0b2NvcnJlbGFjacOzbi4KCiR4X3sxfSQgPSByZW1lc2FzIGVudmlhZGFzIGVuIGVsIG1lcyBwcmV2aW8uCgokeF97Mn0kID0gcmVtZXNhcyBlbnZpYWRhcyBoYWNlIGRvcyBtZXNlcy4KCiR4X3ttfSQgPSByZW1lc2FzIGVudmlhZGFzIGhhY2UgbSBtZXNlcy4KCkVsIHNpZ3VpZW50ZSBjw7NkaWdvIG11ZXN0cmEgY29tbyBjYWxjdWxhciB1bmEgdmFyaWFibGUgY29uIHJlemFnb3MgPSAxCmBgYHtyfQp0cmFpbl9nZHAkbGFnMSA9IGMoTkEsIHRyYWluX2dkcCRyZW1lc2FzWzE6bGVuZ3RoKHRyYWluX2dkcCRyZW1lc2FzKS0xXSkKdHJhaW5fZ2RwCnRlc3RfZ2RwJGxhZzEgPSBjKHRyYWluX2dkcCRyZW1lc2FzW2xlbmd0aCh0cmFpbl9nZHAkcmVtZXNhcyldLCB0ZXN0X2dkcCRyZW1lc2FzWzE6bGVuZ3RoKHRlc3RfZ2RwJHJlbWVzYXMpLTFdKQp0ZXN0X2dkcApgYGAKCiMjIyBmb3VyaWVyCgpgYGB7cn0KZml0X2xtIDwtIHRyYWluX2dkcCB8PgogIG1vZGVsKHRzbG0gPSBUU0xNKGxvZyhyZW1lc2FzKSB+IGZvdXJpZXIoSz0yKSApKQoKcmVwb3J0KGZpdF9sbSkKYGBgCgpgYGB7cn0KcGxvdF9sbSA9IGF1Z21lbnQoZml0X2xtKSB8PgogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcmVtZXNhcywgY29sb3VyID0gInJlYWxlcyIpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3VyID0gImFqdXN0YWRvcyIpKSArCiAgbGFicyh5ID0gTlVMTCwKICAgIHRpdGxlID0gInJlbWVzYXMiCiAgKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9IE5VTEwpKQpnZ3Bsb3RseShwbG90X2xtKQpgYGAKCiMjIyBGb3VyaWVyCgpgYGB7cn0KZml0X2xtIDwtIHRyYWluX2dkcCB8PgogIG1vZGVsKHRzbG0gPSBUU0xNKGxvZyhyZW1lc2FzKSB+IHRyZW5kKCkgKyBmb3VyaWVyKEs9NikgKSkKCnJlcG9ydChmaXRfbG0pCmBgYAoKYGBge3J9CnBsb3RfbG0gPSBhdWdtZW50KGZpdF9sbSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBkYXRlKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHJlbWVzYXMsIGNvbG91ciA9ICJyZWFsZXMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJhanVzdGFkb3MiKSkgKwogIGxhYnMoeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJyZW1lc2FzIgogICkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkKZ2dwbG90bHkocGxvdF9sbSkKYGBgCgoKIyMgUmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlCgpgYGB7cn0KZml0X2xtIDwtIHRyYWluX2dkcCB8PgogIG1vZGVsKHRzbG0gPSBUU0xNKGxvZyhyZW1lc2FzKSB+IHRyZW5kKCkgKyBzZWFzb24oKSArIGdkcCArIGNyaXNpc2ggKyBjcmlzaXNjICsgZm91cmllcihLID0gMikpKQoKcmVwb3J0KGZpdF9sbSkKYGBgCgojIyMgU2VsZWNjacOzbiBkZSB2YXJpYWJsZXMKCmBgYHtyfQpnbGFuY2UoZml0X2xtKSB8PgogIHNlbGVjdChhZGpfcl9zcXVhcmVkLCBDViwgQUlDLCBBSUNjLCBCSUMpCmBgYAoKYGBge3J9CmZpdF9sbSA8LSB0cmFpbl9nZHAgfD4KICBtb2RlbCh0c2xtID0gVFNMTShsb2cocmVtZXNhcykgfiB0cmVuZCgpICsgc2Vhc29uKCkgKyBnZHAgKyBjcmlzaXNoICsgY3Jpc2lzYyArIGxhZzEpKQoKcmVwb3J0KGZpdF9sbSkKCmdsYW5jZShmaXRfbG0pIHw+CiAgc2VsZWN0KGFkal9yX3NxdWFyZWQsIENWLCBBSUMsIEFJQ2MsIEJJQykKYGBgCgoKIyMjIEFuw6FsaXNpcyBkZSByZXNpZHVhbGVzCgpgYGB7cn0KZml0X2xtICU+JSBnZ190c3Jlc2lkdWFscygpCmBgYAojIyA4KSBTdWF2aXphY2nDs24gZXhwb25lbmNpYWwKCiMjIyBTaW1wbGUKCmBgYHtyfQpmaXRfZXMgPC0gdHJhaW4gfD4KICBtb2RlbChFVFModmFsdWUgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiTiIpKSkKCnJlcG9ydChmaXRfZXMpCmBgYApBbHBoYSBlcyBkZSAwLjU4LCBsbyBxdcOpIGluZGljYSBxdWUgbGEgw7psdGltYSBvYnNlcnZhY2nDs24gbm8gZXMgbGEgbcOhcyBpbXBvcnRhbnRlIHkgbGFzIHBhc2FkYXMgb2JzZXJ2YWNpb25lcyB0aWVuZW4gdW4gcGVzbyBzaWduaWZpY2F0aXZvCgpBSUNjIGVzIDU3MDUKCgpgYGB7cn0KcGxvdF9sbSA9IGF1Z21lbnQoZml0X2VzKSB8PgogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gdmFsdWUsIGNvbG91ciA9ICJyZWFsZXMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJhanVzdGFkb3MiKSkgKwogIGxhYnMoeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJyZW1lc2FzIgogICkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkKZ2dwbG90bHkocGxvdF9sbSkKYGBgCgoKIyMjIENvbiB0ZW5kZW5jaWEKCmBgYHtyfQpmaXRfZXMgPC0gdHJhaW4gfD4KICBtb2RlbChBQU4gPSBFVFModmFsdWUgfiBlcnJvcigiQSIpICsgdHJlbmQoIkEiKSArIHNlYXNvbigiTiIpKSkKCnJlcG9ydChmaXRfZXMpCmBgYApgYGB7cn0KcGxvdF9sbSA9IGF1Z21lbnQoZml0X2VzKSB8PgogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gdmFsdWUsIGNvbG91ciA9ICJyZWFsZXMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJhanVzdGFkb3MiKSkgKwogIGxhYnMoeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJyZW1lc2FzIgogICkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkKZ2dwbG90bHkocGxvdF9sbSkKYGBgCgpgYGB7cn0KZml0X2VzIDwtIHRyYWluIHw+CiAgbW9kZWwoRGFtcGVkID0gRVRTKHZhbHVlIH4gZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIpICsgc2Vhc29uKCJOIikpKQoKcmVwb3J0KGZpdF9lcykKYGBgCgpgYGB7cn0KcGxvdF9sbSA9IGF1Z21lbnQoZml0X2VzKSB8PgogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gdmFsdWUsIGNvbG91ciA9ICJyZWFsZXMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJhanVzdGFkb3MiKSkgKwogIGxhYnMoeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJyZW1lc2FzIgogICkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkKZ2dwbG90bHkocGxvdF9sbSkKYGBgCgoKIyMjIENvbiBlc3RhY2lvbmFsaWRhZAoKCmBgYHtyfQpmaXRfZXMgPC0gdHJhaW4gfD4KICBtb2RlbChBQUEgPSBFVFModmFsdWUgfiBlcnJvcigiQSIpICsgdHJlbmQoIkEiKSArIHNlYXNvbigiQSIpKSkKCnJlcG9ydChmaXRfZXMpCmBgYApgYGB7cn0KcGxvdF9sbSA9IGF1Z21lbnQoZml0X2VzKSB8PgogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gdmFsdWUsIGNvbG91ciA9ICJyZWFsZXMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJhanVzdGFkb3MiKSkgKwogIGxhYnMoeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJyZW1lc2FzIgogICkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkKZ2dwbG90bHkocGxvdF9sbSkKYGBgCgpgYGB7cn0KdHJhaW4KYGBgCgoKCmBgYHtyfQpmaXRfZXMgPC0gdHJhaW4gfD4KICBtb2RlbChBQUEgPSBFVFModmFsdWUgfiBlcnJvcigiTSIpICsgdHJlbmQoIkEiKSArIHNlYXNvbigiTSIpKSkKCnJlcG9ydChmaXRfZXMpCmBgYAoKYGBge3J9CnBsb3RfbG0gPSBhdWdtZW50KGZpdF9lcykgfD4KICBnZ3Bsb3QoYWVzKHggPSBkYXRlKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHZhbHVlLCBjb2xvdXIgPSAicmVhbGVzIikpICsKICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvdXIgPSAiYWp1c3RhZG9zIikpICsKICBsYWJzKHkgPSBOVUxMLAogICAgdGl0bGUgPSAicmVtZXNhcyIKICApICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpCmdncGxvdGx5KHBsb3RfbG0pCmBgYAoKIyMjIFNlbGVjY2nDs24gZGVsIG1vZGVsbwoKYGBge3J9CmZpdF9lcyA8LSB0cmFpbiB8PgogIG1vZGVsKEVUUyh2YWx1ZSkpCgpyZXBvcnQoZml0X2VzKQpgYGAKCgpgYGB7cn0KY29tcG9uZW50cyhmaXRfZXMpIHw+CiAgYXV0b3Bsb3QoKQpgYGAKCgojIyMgUHJvbm9zdGljbywgaW50ZXJ2YWxvIGRlIHByZWRpY2Npw7NuIHkgYW7DoWxpc2lzIGRlIHJlc2lkdWFsZXMKCgpgYGB7cn0KZnJjc3QgPSBmaXRfZXMgJT4lIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKQoKZml0X2VzICU+JQogIGZvcmVjYXN0KGggPSB0c3RuZ19wcmRzKSAlPiUKICBhdXRvcGxvdCh0cmFpbikKYGBgCmBgYHtyfQpmcmNzdApgYGAKCmBgYHtyfQpmcmNzdCAlPiUgaGlsbyhsZXZlbCA9IGMoODAsIDk1KSkKYGBgCgoKYGBge3J9CmFjY3VyYWN5KGZpdF9lcykKYGBgCgoKYGBge3J9CmFjY3VyYWN5KGZpdF9sbSkKYGBgCgpgYGB7cn0KZml0X2VzICU+JSBnZ190c3Jlc2lkdWFscygpCmBgYAoKCg==